سؤال

ما هي أسهل طريقة لفصل JPA Entity Bean المحدد الذي تم الحصول عليه من خلال EntityManager.وبدلاً من ذلك، هل يمكنني الحصول على استعلام يعرض كائنات منفصلة في المقام الأول بحيث تعمل بشكل أساسي كـ "للقراءة فقط"؟

السبب وراء رغبتي في القيام بذلك هو أنني أرغب في تعديل البيانات داخل الحبة - في تطبيقي فقط، ولكن لم يتم استمرارها في قاعدة البيانات على الإطلاق.في برنامجي، لا بد لي في النهاية من استدعاء Flush() على EntityManager، والذي سيستمر في جميع التغييرات من الكيانات المرفقة إلى قاعدة البيانات السفلية، لكنني أريد استبعاد كائنات محددة.

هل كانت مفيدة؟

المحلول

لسوء الحظ، لا توجد طريقة لفصل كائن واحد عن مدير الكيان في تطبيق JPA الحالي، AFAIR.

سيتم قطع اتصال EntityManager.clear() الجميع كائنات JPA، لذلك قد لا يكون هذا حلاً مناسبًا في جميع الحالات، إذا كان لديك كائنات أخرى تخطط للإبقاء عليها متصلة.

لذا فإن أفضل رهان لك هو استنساخ الكائنات وتمرير النسخ إلى الكود الذي يغير الكائنات.نظرًا لأنه يتم الاهتمام بحقول الكائنات البدائية وغير القابلة للتغيير من خلال آلية الاستنساخ الافتراضية بطريقة مناسبة، فلن تضطر إلى كتابة الكثير من أكواد السباكة (بصرف النظر عن الاستنساخ العميق لأي هياكل مجمعة قد تكون لديك).

نصائح أخرى

(قد يكون الوقت متأخرًا جدًا للإجابة، ولكنه قد يكون مفيدًا للآخرين)

أقوم الآن بتطوير نظامي الأول باستخدام JPA.لسوء الحظ أواجه هذه المشكلة عندما يكون هذا النظام على وشك الاكتمال.

ببساطة.استخدم السبات، أو انتظر JPA 2.0.

في وضع السبات، يمكنك استخدام "session.evict(object)" لإزالة كائن واحد من الجلسة.في جبا 2.0، في المسودة الآن, توجد طريقة "EntityManager.detach(object)" لفصل كائن واحد عن سياق الاستمرارية.

بغض النظر عن تطبيق JPA الذي تستخدمه، ما عليك سوى استخدامه entityManager.detach(object) إنه الآن في JPA 2.0 وجزء من JEE6.

إذا كنت بحاجة إلى فصل كائن من EntityManager وكنت تستخدم Hibernate كطبقة ORM الأساسية، فيمكنك الوصول إلى جلسة السبات الكائن واستخدام جلسة العمل (الكائن) الطريقة التي ذكرها موريسيو كانادا أعلاه.

public void detach(Object entity) {
    org.hibernate.Session session = (Session) entityManager.getDelegate();
    session.evict(entity);
}

بالطبع قد ينقطع هذا إذا قمت بالتبديل إلى موفر ORM آخر ولكن أعتقد أن هذا يفضل محاولة عمل نسخة عميقة.

بقدر ما أعرف، فإن الطرق المباشرة الوحيدة للقيام بذلك هي:

  1. الالتزام بـ txn - ربما لا يكون خيارًا معقولًا
  2. امسح سياق الثبات - EntityManager.clear() - هذا أمر وحشي، لكنه سيزيله
  3. انسخ الكائن - في أغلب الأحيان تكون كائنات JPA الخاصة بك قابلة للتسلسل، لذلك يجب أن يكون هذا سهلاً (إن لم يكن فعالاً بشكل خاص).

في حالة استخدام EclipseLink لديك أيضًا الخيارات،

استخدم تلميح الاستعلام، eclipselink.maintain-cache"="false - سيتم فصل كافة الكائنات التي تم إرجاعها.

استخدم ال EclipseLink JpaEntityManager copy() API لنسخ الكائن إلى العمق المطلوب.

إذا لم يكن هناك الكثير من الخصائص في الحبة، فقد تقوم فقط بإنشاء مثيل جديد وتعيين جميع خصائصه يدويًا من الحبة المستمرة.

يمكن تنفيذ ذلك كمنشئ نسخة، على سبيل المثال:

public Thing(Thing oldBean) {
  this.setPropertyOne(oldBean.getPropertyOne());
  // and so on
}

ثم:

Thing newBean = new Thing(oldBean);

يعد هذا أمرًا سريعًا وقذرًا، ولكن يمكنك أيضًا إجراء تسلسل للكائن وإلغاء تسلسله.

نظرًا لأنني أستخدم SEAM وJPA 1.0 ونظامي لديه دقة تحتاج إلى تسجيل جميع تغييرات الحقول، فقد قمت بإنشاء كائن قيمة أو كائن نقل بيانات إذا كانت نفس حقول الكيان التي تحتاج إلى تسجيل.مُنشئ البوجو الجديد هو:

    public DocumentoAntigoDTO(Documento documentoAtual) {
    Method[] metodosDocumento = Documento.class.getMethods();
    for(Method metodo:metodosDocumento){
        if(metodo.getName().contains("get")){
            try {
                Object resultadoInvoke = metodo.invoke(documentoAtual,null);
                Method[] metodosDocumentoAntigo = DocumentoAntigoDTO.class.getMethods();
                for(Method metodoAntigo : metodosDocumentoAntigo){
                    String metodSetName = "set" + metodo.getName().substring(3);
                    if(metodoAntigo.getName().equals(metodSetName)){
                        metodoAntigo.invoke(this, resultadoInvoke);
                    }
                }
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
}

في JPA 1.0 (تم اختباره باستخدام EclipseLink) يمكنك استرداد الكيان خارج المعاملة.على سبيل المثال، مع المعاملات المُدارة بالحاويات، يمكنك القيام بما يلي:

public MyEntity myMethod(long id) {
    final MyEntity myEntity = retrieve(id);
    // myEntity is detached here
}

@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public MyEntity retrieve(long id) {
    return entityManager.find(MyEntity.class, id);
}

تعامل مع حالة مماثلة لقد قمت بإنشاء كائن DTO يقوم بتوسيع كائن الكيان المستمر كما يلي:

class MyEntity
{
   public static class MyEntityDO extends MyEntity {}

}

أخيرًا، سوف يسترد الاستعلام العددي السمات غير المُدارة المطلوبة:

(Hibernate) select p.id, p.name from MyEntity P
(JPA)       select new MyEntity(p.id, p.name) from myEntity P

إذا وصلت إلى هنا لأنك تريد بالفعل تمرير كيان عبر حدود بعيدة، فما عليك سوى وضع بعض التعليمات البرمجية لخداع السبات.

for(RssItem i : result.getChannel().getItem()){
}

لن يعمل Cloneable لأنه يقوم بالفعل بنسخ PersistantBag عبره.

وننسى استخدام التدفقات القابلة للتسلسل وbytearray والتدفقات عبر الأنابيب.يؤدي إنشاء سلاسل الرسائل لتجنب الجمود إلى قتل المفهوم بأكمله.

أعتقد أن هناك طريقة لطرد كيان واحد من EntityManager عن طريق استدعاء هذا

EntityManagerFactory emf;
emf.getCache().evict(Entity);

سيؤدي هذا إلى إزالة كيان معين من ذاكرة التخزين المؤقت.

أعتقد أنه يمكنك أيضًا استخدام الطريقة EntityManager.refresh(Object o) إذا لم يتم تغيير المفتاح الأساسي للكيان.ستعمل هذه الطريقة على استعادة الحالة الأصلية للكيان.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top