জেপিএ সহ এনটিটিম্যানেজ.ফাইন্ড () বনাম এন্টিমেজেনজেটরেট রেফারেন্স () কখন ব্যবহার করবেন


103

আমি এমন একটি পরিস্থিতি নিয়ে এসেছি (যা আমি মনে করি অদ্ভুত তবে সম্ভবত এটি বেশ স্বাভাবিক) যেখানে আমি একটি ডাটাবেস সত্তা পাওয়ার জন্য EntityManager.getReferences (LObj.getClass (), LObj.getId ()) ব্যবহার করি এবং তারপরে ফিরে আসা বস্তুটি পাস করি অন্য টেবিলে জেদ থাকে।

সুতরাং মূলত প্রবাহটি এরকম ছিল:

TFacade ক্লাস

  createT (FObj, AObj) {
    টি তোবজ = নতুন টি ();
    TObj.setF (FObj);
    TObj.setA (AObj);
    ...
    EntityManager.persist (TObj);
    ...
    L LObj = A.getL ();
    FObj.setL (LObj);
    FFacade.editF (FObj);
  }
}

@ TransactionAttributeType.REQUIRES_NEW
ক্লাস FFacade {

  editF (FObj) {
    L LObj = FObj.getL ();
    LObj = EntityManager.getReferences (LObj.getClass (), LObj.getId ());
    ...
    EntityManager.merge (FObj);
    ...
    FLHFacade.create (FObj, LObj);
  }
}

@ TransactionAttributeType.REQUIRED
ক্লাস FLHFacade ac

  createFLH (FObj, LObj) {
    FLH FLHObj = নতুন এফএলএইচ ();
    FLHObj.setF (FObj);
    FLHObj.setL (LObj);
    ....
    EntityManager.persist (FLHObj);
    ...
  }
}

আমি নিম্নলিখিত ব্যতিক্রমটি পেয়ে যাচ্ছিলাম "java.lang.IllegalArgumentException: অজানা সত্তা: com.my.persistance.L $$ EnhancerByCGLIB e 3e7987d0"

এটি কিছুক্ষণ দেখার পরে, অবশেষে আমি বুঝতে পারলাম যে এটি হ'ল এজেন্টি ম্যানেজ.আররেট রেফারেন্স () পদ্ধতিটি ব্যবহার করছিলাম যেহেতু পদ্ধতিটি প্রক্সি ফেরত দেওয়ার কারণে আমি উপরের ব্যতিক্রম পাচ্ছিলাম।

এটি আমাকে অবাক করে দেয়, কখন এটিএনটিজনেজ.ফাইন্ড () পদ্ধতির পরিবর্তে এনটিটি ম্যানেজ.আরজেট রেফারেন্স () পদ্ধতিটি ব্যবহার করার পরামর্শ দেওয়া হয় ?

সত্তা ম্যানেজ.জেট রেফারেন্স () একটি সত্ত্বা নটফাউন্ডএক্সসেপশন নিক্ষেপ করে যদি এটি সত্তা সন্ধান করতে না পারে যা নিজেই খুব সুবিধাজনক। EntityManager.find () পদ্ধতিটি সত্তাটি খুঁজে না পারলে কেবল শূন্য হয়।

লেনদেনের সীমানা সম্পর্কিত, আমার কাছে মনে হচ্ছে সদ্য পাওয়া সত্তাটিকে নতুন লেনদেনে পাস করার আগে আপনাকে অনুসন্ধান () পদ্ধতিটি ব্যবহার করা দরকার। আপনি যদি getReferences () পদ্ধতিটি ব্যবহার করেন তবে সম্ভবত আপনি আমার উপরের ব্যতিক্রমের মতো একই পরিস্থিতিতে পড়বেন।


উল্লেখ করতে ভুলে গেছি, আমি হাইপারনেটকে জেপিএ সরবরাহকারী হিসাবে ব্যবহার করছি।
সিবজারটি

উত্তর:


152

যখন সাধারণত আমার ডাটাবেস স্থিতি অ্যাক্সেস করার প্রয়োজন হয় না তখন আমি getReferences পদ্ধতি ব্যবহার করি (আমি বুঝি গেটর পদ্ধতি)। শুধু রাষ্ট্র পরিবর্তন করতে (মানে সেটার পদ্ধতিটি)। আপনার জানা উচিত, getReferences একটি প্রক্সি অবজেক্ট ফেরত দেয় যা একটি শক্তিশালী বৈশিষ্ট্য ব্যবহার করে যা স্বয়ংক্রিয় নোংরা চেকিং বলে। ধরুন নিচেরটি

public class Person {

    private String name;
    private Integer age;

}


public class PersonServiceImpl implements PersonService {

    public void changeAge(Integer personId, Integer newAge) {
        Person person = em.getReference(Person.class, personId);

        // person is a proxy
        person.setAge(newAge);
    }

}

তাহলে আমি কি কল এটি পদ্ধতি, JPA প্রদানকারী, লোকচক্ষুর অন্তরালে, ডাকব

SELECT NAME, AGE FROM PERSON WHERE PERSON_ID = ?

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ?

যদি আমি getReferences পদ্ধতি কল করি , পর্দার পিছনে JPA সরবরাহকারী কল করবে

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ?

আর তুমি জানো কেন ???

আপনি যখন getReferences কল করবেন, আপনি একটি প্রক্সি অবজেক্ট পাবেন। এর মতো কিছু (জেপিএ সরবরাহকারী এই প্রক্সিটি বাস্তবায়নের যত্ন নেয়)

public class PersonProxy {

    // JPA provider sets up this field when you call getReference
    private Integer personId;

    private String query = "UPDATE PERSON SET ";

    private boolean stateChanged = false;

    public void setAge(Integer newAge) {
        stateChanged = true;

        query += query + "AGE = " + newAge;
    }

}

সুতরাং লেনদেন প্রতিশ্রুতি দেওয়ার আগে, জেপিএ সরবরাহকারী ব্যক্তি সত্তা আপডেট করতে বা না করার জন্য রাষ্ট্রের পরিবর্তিত পতাকা দেখতে পাবে। আপডেটের বিবৃতি দেওয়ার পরে যদি কোনও সারি আপডেট না করা হয় তবে জেপিএ সরবরাহকারী জেপিএ স্পেসিফিকেশন অনুযায়ী এনটিটি নটফাউন্ডএক্সসেপশন নিক্ষেপ করবে।

শুভেচ্ছান্তে,


4
আমি EclipseLink 2.5.0 ব্যবহার করছি এবং উপরে বর্ণিত প্রশ্নগুলি সঠিক নয়। এটি সর্বদা SELECTআগে জারি করে UPDATE, আমি find()/ যে কোনটি getReference()ব্যবহার করি না কেন । সবচেয়ে SELECTখারাপটি হ'ল নন-এলজি সম্পর্ককে (নতুন জারি করা SELECTS) ট্র্যাভার করে , যদিও আমি কেবল একটি সত্তায় একটি ক্ষেত্র আপডেট করতে চাই।
দেজন মিলোসেভিক

1
@ আর্থার রোনাল্ড যদি getReferences নামে পরিচিত সত্তায় কোনও সংস্করণ টীকা থাকে তবে কী হবে?
ডেভিড হাফম্যান

আমার @ ডিজনমিলোসেভিকের মতো একই সমস্যা রয়েছে: getReferences () এর মাধ্যমে প্রাপ্ত কোনও সত্তা অপসারণ করার সময়, সেই সত্তার উপর একটি নির্বাচন জারি করা হয় এবং এটি সেই সত্তার সমস্ত আলস্য সম্পর্ককে অনুসরণ করে, এভাবে অনেকগুলি ইলেক্ট্রেস (ইক্লিপস লিংক ২.০.০ সহ) জারি করে।
স্টাফেন অ্যাপারসেল 4'17

27

যেমনটি আমি এই নিবন্ধে ব্যাখ্যা করেছি , ধরে নিলাম আপনার নিম্নোক্ত চিত্রটিতে যেমন একটি পিতামাতৃ Postসত্তা এবং একটি শিশু রয়েছে PostComment:

এখানে চিত্র বর্ণনা লিখুন

আপনি সমিতি findস্থাপনের চেষ্টা করার পরে যদি কল করেন @ManyToOne post:

PostComment comment = new PostComment();
comment.setReview("Just awesome!");

Post post = entityManager.find(Post.class, 1L);
comment.setPost(post);

entityManager.persist(comment);

হাইবারনেট নিম্নলিখিত বিবৃতিগুলি কার্যকর করবে:

SELECT p.id AS id1_0_0_,
       p.title AS title2_0_0_
FROM   post p
WHERE p.id = 1

INSERT INTO post_comment (post_id, review, id)
VALUES (1, 'Just awesome!', 1)

এবার নির্বাচন করুন ক্যোয়ারীটি বেহুদা কারণ আমাদের পোস্ট সত্তা আনার দরকার নেই। আমরা কেবল অন্তর্নিহিত পোস্ট_আইডি বিদেশী কী কলামটি সেট করতে চাই।

এখন, আপনি যদি এর getReferenceপরিবর্তে ব্যবহার করেন :

PostComment comment = new PostComment();
comment.setReview("Just awesome!");

Post post = entityManager.getReference(Post.class, 1L);
comment.setPost(post);

entityManager.persist(comment);

এবার হাইবারনেট কেবল ইনসার্ট স্টেটমেন্ট জারি করবে:

INSERT INTO post_comment (post_id, review, id)
VALUES (1, 'Just awesome!', 1)

বিপরীতে find, getReferenceএকমাত্র একটি সত্তা প্রক্সি প্রদান করে যার কেবল সনাক্তকারী সেট রয়েছে। আপনি যদি প্রক্সি অ্যাক্সেস করেন, সত্ত্বেও এনটিটি ম্যানেজারটি খোলা থাকার সাথে সম্পর্কিত এসকিউএল স্টেটমেন্টটি ট্রিগার করা হবে।

যাইহোক, এই ক্ষেত্রে, আমাদের সত্তা প্রক্সিটি অ্যাক্সেস করার দরকার নেই। আমরা কেবল অন্তর্নিহিত সারণী রেকর্ডে বিদেশী কী প্রচার করতে চাই তাই এই ব্যবহারের ক্ষেত্রে প্রক্সি লোড করা যথেষ্ট।

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


1
আমাদের এটি জানার জন্য ভ্লাদ ধন্যবাদ! তবে জাভাদোকের মতে এগুলি বিরক্তিকর বলে মনে হচ্ছে: "যখন জিগ্রিফারেন্স বলা হয় তখন অধ্যবসায়ী সরবরাহকারীর রানটাইমকে এন্টিটি নটফাউন্ডএক্সসেপশন নিক্ষেপ করার অনুমতি দেওয়া হয়"। এটি একটি নির্বাচন (কমপক্ষে সারিটির অস্তিত্ব পরীক্ষা করার জন্য) ছাড়া সম্ভব নয়, তাই না? সুতরাং একটি শেষ পর্যন্ত নির্বাচন বাস্তবায়নের উপর নির্ভর করে।
adrhc

3
আপনার বর্ণিত ব্যবহারের ক্ষেত্রে, হাইবারনেট hibernate.jpa.compliance.proxyকনফিগারেশন সম্পত্তি সরবরাহ করে , তাই আপনি জেপিএ সম্মতি বা আরও ভাল ডেটা অ্যাক্সেস কর্মক্ষমতা চয়ন করতে পারেন।
ভ্লাদ মিহলসিয়া

@ ভ্লাদমিহালসিয়া কেন getReferenceপিকে সেট দিয়ে মডেলটির নতুন উদাহরণ স্থাপন করার পক্ষে যথেষ্ট তা হলে কেন এগুলি প্রয়োজন। আমি কী মিস করছি?
রিলাবি

এটি কেবলমাত্র হাইবারনারিয়ায় সমর্থিত যদি ট্র্যাভার করা হয় তবে আপনাকে সমিতি লোড করতে দেয় না।
ভ্লাদ মিহলসিয়া

8

কারণ একটি রেফারেন্স 'ম্যানেজড', তবে হাইড্রেটেড নয়, এটি আপনাকে প্রথমে স্মৃতিতে লোড করার প্রয়োজন ছাড়াই আইডি দ্বারা কোনও সত্তা সরিয়ে ফেলতে দেয়।

যেহেতু আপনি কোনও ব্যবস্থাবিহীন সত্তাটি সরাতে পারবেন না, কেবল তাত্ক্ষণিকভাবে মুছে ফেলার জন্য এটি সন্ধান (()) বা ক্রিয়েটকিউরি (...) ব্যবহার করে সমস্ত ক্ষেত্র লোড করা কেবল সহজ বোকা।

MyLargeObject myObject = em.getReference(MyLargeObject.class, objectId);
em.remove(myObject);

7

এটি আমাকে অবাক করে দেয়, কখন এটিএনটিজনেজ.ফাইন্ড () পদ্ধতির পরিবর্তে এনটিটি ম্যানেজ.আরজেট রেফারেন্স () পদ্ধতিটি ব্যবহার করার পরামর্শ দেওয়া হয়?

EntityManager.getReference()আসলেই একটি ত্রুটিযুক্ত প্রবণ পদ্ধতি এবং সত্যিই খুব কম কেস পাওয়া যায় যেখানে ক্লায়েন্ট কোড ব্যবহার করা দরকার।
ব্যক্তিগতভাবে, আমার কখনই এটি ব্যবহার করার দরকার নেই।

EntityManager.getReferences () এবং EntityManager.find (): ওভারহেডের ক্ষেত্রে কোনও পার্থক্য নেই

আমি গৃহীত উত্তর এবং বিশেষত:

তাহলে আমি কি কল এটি পদ্ধতি, JPA প্রদানকারী, লোকচক্ষুর অন্তরালে, ডাকব

SELECT NAME, AGE FROM PERSON WHERE PERSON_ID = ?

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ?

যদি আমি getReferences পদ্ধতি কল করি , পর্দার পিছনে JPA সরবরাহকারী কল করবে

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ?

হাইবারনেট 5 এর সাথে আমি যে আচরণ করি getReference()তা নয় এবং জাভাদোক এমন কথা বলেন না:

একটি উদাহরণ পান, যার রাজ্যে আলস্যভাবে আনতে পারে। যদি অনুরোধ করা দৃষ্টান্তটি ডাটাবেসে উপস্থিত না থাকে, উদাহরণস্বরূপ রাষ্ট্রটি প্রথম অ্যাক্সেস করা হলে অ্যান্টিটি নটফাউন্ডএক্সসেপশন নিক্ষেপ করা হয়। (যখন জিগ্রিফারেন্স বলা হয় তখন অধ্যবসায়ী প্রদানকারী রানটাইমটি সত্ত্বা নটফাউন্ডএক্সসেপশন নিক্ষেপ করার অনুমতি দেয়)) সত্তা ব্যবস্থাপক খোলা থাকাকালীন অ্যাপ্লিকেশন দ্বারা অ্যাক্সেস না করা হলে অ্যাপ্লিকেশনটি উদাহরণস্বরূপ বিচ্ছিন্নতার উপর উপলব্ধ হবে না The

EntityManager.getReference() সত্তাটি দুটি ক্ষেত্রে পুনরুদ্ধার করতে একটি জিজ্ঞাসা ছাড়ায়:

1) সত্তা যদি দৃistence়তা প্রসঙ্গে সংরক্ষণ করা হয় তবে এটি প্রথম স্তরের ক্যাশে।
এবং এই আচরণটি সুনির্দিষ্ট নয় EntityManager.getReference(), EntityManager.find()সত্তাটি দৃistence়তা প্রসঙ্গে সঞ্চিত থাকলে সত্তাটি পুনরুদ্ধার করার জন্য একটি জিজ্ঞাসাও ছাড়বে।

আপনি কোনও উদাহরণ দিয়ে প্রথম পয়েন্টটি পরীক্ষা করতে পারেন।
আপনি প্রকৃত হাইবারনেট বাস্তবায়নের উপরও নির্ভর করতে পারেন।
প্রকৃতপক্ষে, সত্তাটি লোড করার জন্য শ্রেণীর পদ্ধতির EntityManager.getReference()উপর নির্ভর করে । এটির বাস্তবায়ন এখানে:createProxyIfNecessary()org.hibernate.event.internal.DefaultLoadEventListener

private Object createProxyIfNecessary(
        final LoadEvent event,
        final EntityPersister persister,
        final EntityKey keyToLoad,
        final LoadEventListener.LoadType options,
        final PersistenceContext persistenceContext) {
    Object existing = persistenceContext.getEntity( keyToLoad );
    if ( existing != null ) {
        // return existing object or initialized proxy (unless deleted)
        if ( traceEnabled ) {
            LOG.trace( "Entity found in session cache" );
        }
        if ( options.isCheckDeleted() ) {
            EntityEntry entry = persistenceContext.getEntry( existing );
            Status status = entry.getStatus();
            if ( status == Status.DELETED || status == Status.GONE ) {
                return null;
            }
        }
        return existing;
    }
    if ( traceEnabled ) {
        LOG.trace( "Creating new proxy for entity" );
    }
    // return new uninitialized proxy
    Object proxy = persister.createProxy( event.getEntityId(), event.getSession() );
    persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey( keyToLoad );
    persistenceContext.addProxy( keyToLoad, proxy );
    return proxy;
}

মজার অংশটি হ'ল:

Object existing = persistenceContext.getEntity( keyToLoad );

2) যদি আমরা কার্যকরভাবে সত্তাকে হস্তান্তরিত না করি, জাভাদোকের অলসভাবে প্রাপ্ত প্রতিধ্বনির প্রতিধ্বনি ।
প্রকৃতপক্ষে, সত্তাটির কার্যকর লোডিং নিশ্চিত করার জন্য, এটিতে কোনও পদ্ধতির আবেদন করা প্রয়োজন।
সুতরাং লাভটি এমন একটি দৃশ্যের সাথে সম্পর্কিত যেখানে আমরা কোনও সত্তাকে ব্যবহারের প্রয়োজন ছাড়াই লোড করতে চাই? অ্যাপ্লিকেশনগুলির ফ্রেমের ক্ষেত্রে, এই প্রয়োজনটি সত্যিই অস্বাভাবিক এবং getReference()এর সাথে পরবর্তী অংশটি পড়লে আচরণটিও খুব বিভ্রান্তিকর।

কেন সত্তা ম্যানেজ.আরজিফারেন্স রেফারেন্স () এর চেয়ে সত্তা ম্যানেজ.আরফাইন্ড ()

ওভারহেডের দিক থেকে, আগের পয়েন্টে আলোচিত getReference()চেয়ে ভাল নয় find()
তাহলে কেন এক বা অন্যটি ব্যবহার করবেন?

আমন্ত্রণ করা getReference()একটি অলস ফিচার সত্তা ফিরিয়ে দিতে পারে।
এখানে, অলস আনয়ন সত্তার সাথে সম্পর্কিত নয় বরং সত্তার সাথে সম্পর্কিত।
এর অর্থ হল যে আমরা যদি প্রার্থনা করি getReference()এবং তারপরে দৃistence়তা প্রসঙ্গটি বন্ধ হয়ে যায়, সত্তাটি কখনই লোড করা যায় না এবং ফলস্বরূপ ফলাফলটি অনির্দেশ্য। উদাহরণস্বরূপ যদি প্রক্সি অবজেক্টটি সিরিয়ালযুক্ত হয়, আপনি nullসিরিয়ালাইজড ফলাফল হিসাবে একটি রেফারেন্স পেতে পারেন বা যদি কোনও পদ্ধতি প্রক্সি অবজেক্টে ডাকা হয় তবে একটি ব্যতিক্রম যেমন LazyInitializationExceptionনিক্ষেপ করা হয়।

এর অর্থ হ'ল এটি নিক্ষেপ EntityNotFoundExceptionকরাই হ'ল মূল কারণ হ'ল getReference()ডাটাবেসে উপস্থিত নেই এমন একটি দৃষ্টান্ত হ্যান্ডেল করার জন্য যা ত্রুটি পরিস্থিতিটি উপস্থিত না থাকলে কখনও সম্পাদন করা যায় না।

EntityManager.find()EntityNotFoundExceptionসত্তাটি পাওয়া না গেলে নিক্ষেপের উচ্চাকাঙ্ক্ষা থাকে না। এর আচরণ সহজ এবং পরিষ্কার উভয়ই। আপনি কখনই বিস্মিত হবেন না কারণ এটি সর্বদা একটি লোড সত্তা বা null(যদি সত্তাটি পাওয়া যায় না) ফিরে আসে তবে কোনও প্রক্সি আকারে কোনও সত্তা কার্যকরভাবে লোড নাও হতে পারে never
তাই EntityManager.find()খুব বেশিরভাগ ক্ষেত্রে পক্ষপাতী হওয়া উচিত।


গ্রহণযোগ্য প্রতিক্রিয়া + ভ্লাদ মিহালসিয়া প্রতিক্রিয়া + ভ্লাদ মিহালসিয়ায় আমার মন্তব্য (এটি শেষের চেয়ে কম গুরুত্বপূর্ণ হতে পারে) এর সাথে তুলনা করার সময় আপনার কারণটি বিভ্রান্তিকর।
এডিআরসিসি

1
প্রো জেপিএ 2 উল্লেখ করেছে: "খুব নির্দিষ্ট পরিস্থিতি দেওয়া যাতে getReferences () ব্যবহার করা যেতে পারে, সন্ধান করুন () কার্যত সমস্ত ক্ষেত্রে ব্যবহার করা উচিত"।
JL_SO

এই প্রশ্নের উত্তর দাও কারণ এটি স্বীকৃত উত্তরের প্রয়োজনীয় পরিপূরক এবং কারণ আমার পরীক্ষাগুলি প্রমাণ করে যে সত্তা প্রক্সিটির কোনও সম্পত্তি নির্ধারণ করার সময় এটি ডাটাবেস থেকে প্রাপ্ত, গৃহীত উত্তর যা বলে তার বিপরীতে। ভ্লাদের দ্বারা বর্ণিত কেসটিই আমার পরীক্ষাগুলি পাস করেছে।
João এফ

2

আমি নির্বাচিত উত্তরের সাথে একমত নই, এবং ডেভিডএক্সএক্সএক্সএক্সএক্স হিসাবে সঠিকভাবে উল্লেখ করা হয়েছে, getReferences নির্বাচন ছাড়াই গতিশীল আপডেটের এমন আচরণ সরবরাহ করে না। আমি এই উত্তরের বৈধতা সম্পর্কিত একটি প্রশ্ন জিজ্ঞাসা করেছি, এখানে দেখুন - হাইবারনেট জেপিএর getReferences () এর পরে সেটার ব্যবহার করে সিলেক্ট না করে আপডেট করা যাবে না

আমি পুরোপুরি সত্যই এমন কাউকে দেখিনি যে প্রকৃতপক্ষে সেই কার্যকারিতাটি ব্যবহার করেছে। যেখানে মন চায়। আমি কেন বুঝতে পারি না কেন এটি এতটা উপরে উঠেছে।

এখন সবার আগে, আপনি হাইবারনেট প্রক্সি অবজেক্ট, একটি সেটার বা গেটরকে যাই বলুন না কেন, একটি এসকিউএল বরখাস্ত করা হয় এবং বস্তুটি লোড হয়।

তবে আমি ভেবেছিলাম, তাহলে কী হবে যদি জেপিএ getReferences () প্রক্সি সেই কার্যকারিতা সরবরাহ করে না। আমি কেবল আমার নিজস্ব প্রক্সি লিখতে পারি।

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

, USAGE

Order example = ProxyHandler.getReference(Order.class, 3);
example.setType("ABCD");
example.setCost(10);
PersistenceService.save(example);

এবং এটি নিম্নলিখিত প্রশ্নের সন্ধান করবে -

UPDATE Order SET type = 'ABCD' and cost = 10 WHERE id = 3;

এবং আপনি সন্নিবেশ করতে চাইলেও, আপনি এখনও পার্সনিস্টি সার্ভিস.সেইভ করতে পারেন (নতুন অর্ডার ("এ", 2)); এবং এটি একটি sertোকানো উচিত হিসাবে এটি আগুন হবে।

বাস্তবায়ন

এটি আপনার pom.xML- এ যুক্ত করুন

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.10</version>
</dependency>

গতিশীল প্রক্সি তৈরি করতে এই শ্রেণি তৈরি করুন -

@SuppressWarnings("unchecked")
public class ProxyHandler {

public static <T> T getReference(Class<T> classType, Object id) {
    if (!classType.isAnnotationPresent(Entity.class)) {
        throw new ProxyInstantiationException("This is not an entity!");
    }

    try {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(classType);
        enhancer.setCallback(new ProxyMethodInterceptor(classType, id));
        enhancer.setInterfaces((new Class<?>[]{EnhancedProxy.class}));
        return (T) enhancer.create();
    } catch (Exception e) {
        throw new ProxyInstantiationException("Error creating proxy, cause :" + e.getCause());
    }
}

সমস্ত পদ্ধতির সাথে একটি ইন্টারফেস তৈরি করুন -

public interface EnhancedProxy {
    public String getJPQLUpdate();
    public HashMap<String, Object> getModifiedFields();
}

এখন, একটি ইন্টারসেপ্টর তৈরি করুন যা আপনাকে আপনার প্রক্সিতে এই পদ্ধতিগুলি প্রয়োগ করতে দেয় -

import com.anil.app.exception.ProxyInstantiationException;
import javafx.util.Pair;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import javax.persistence.Id;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
/**
* @author Anil Kumar
*/
public class ProxyMethodInterceptor implements MethodInterceptor, EnhancedProxy {

private Object target;
private Object proxy;
private Class classType;
private Pair<String, Object> primaryKey;
private static HashSet<String> enhancedMethods;

ProxyMethodInterceptor(Class classType, Object id) throws IllegalAccessException, InstantiationException {
    this.classType = classType;
    this.target = classType.newInstance();
    this.primaryKey = new Pair<>(getPrimaryKeyField().getName(), id);
}

static {
    enhancedMethods = new HashSet<>();
    for (Method method : EnhancedProxy.class.getDeclaredMethods()) {
        enhancedMethods.add(method.getName());
    }
}

@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    //intercept enhanced methods
    if (enhancedMethods.contains(method.getName())) {
        this.proxy = obj;
        return method.invoke(this, args);
    }
    //else invoke super class method
    else
        return proxy.invokeSuper(obj, args);
}

@Override
public HashMap<String, Object> getModifiedFields() {
    HashMap<String, Object> modifiedFields = new HashMap<>();
    try {
        for (Field field : classType.getDeclaredFields()) {

            field.setAccessible(true);

            Object initialValue = field.get(target);
            Object finalValue = field.get(proxy);

            //put if modified
            if (!Objects.equals(initialValue, finalValue)) {
                modifiedFields.put(field.getName(), finalValue);
            }
        }
    } catch (Exception e) {
        return null;
    }
    return modifiedFields;
}

@Override
public String getJPQLUpdate() {
    HashMap<String, Object> modifiedFields = getModifiedFields();
    if (modifiedFields == null || modifiedFields.isEmpty()) {
        return null;
    }
    StringBuilder fieldsToSet = new StringBuilder();
    for (String field : modifiedFields.keySet()) {
        fieldsToSet.append(field).append(" = :").append(field).append(" and ");
    }
    fieldsToSet.setLength(fieldsToSet.length() - 4);
    return "UPDATE "
            + classType.getSimpleName()
            + " SET "
            + fieldsToSet
            + "WHERE "
            + primaryKey.getKey() + " = " + primaryKey.getValue();
}

private Field getPrimaryKeyField() throws ProxyInstantiationException {
    for (Field field : classType.getDeclaredFields()) {
        field.setAccessible(true);
        if (field.isAnnotationPresent(Id.class))
            return field;
    }
    throw new ProxyInstantiationException("Entity class doesn't have a primary key!");
}
}

এবং ব্যতিক্রম শ্রেণি -

public class ProxyInstantiationException extends RuntimeException {
public ProxyInstantiationException(String message) {
    super(message);
}

এই প্রক্সি ব্যবহার করে সংরক্ষণ করার জন্য একটি পরিষেবা -

@Service
public class PersistenceService {

@PersistenceContext
private EntityManager em;

@Transactional
private void save(Object entity) {
    // update entity for proxies
    if (entity instanceof EnhancedProxy) {
        EnhancedProxy proxy = (EnhancedProxy) entity;
        Query updateQuery = em.createQuery(proxy.getJPQLUpdate());
        for (Entry<String, Object> entry : proxy.getModifiedFields().entrySet()) {
            updateQuery.setParameter(entry.getKey(), entry.getValue());
        }
        updateQuery.executeUpdate();
    // insert otherwise
    } else {
        em.persist(entity);
    }

}
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.