-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 
Author Message
 Post subject: Lebensende EntityManager bei JEE
PostPosted: Fri Jan 09, 2009 4:03 pm 
Newbie

Joined: Fri Jan 09, 2009 3:04 pm
Posts: 2
Hallo,

Ich beschäftige mich mit EJBs, setze als App.Server JBoss 4.2.2GA (Hibernate ist da ja schon dabei) ein und habe als Datenbank MySQL 5.0.

Eine Applikation braucht pessimistische Locks.

Ein Aufruf sieht ähnlich wie folgt aus:
Client --> ServiceBean -->* ModifierBean --> DB

wobei:
ServiceBean = einfaches Stateless Session Bean mit ein wenig Logik
ModifierBean= Stateless Session Bean mit Datenbankzugriff (und Locking)

Für meinen Testfall habe ich mal die Logikabläufe aus dem ServiceBean weggelassen und nur 2x modify() aufgerufen (siehe Quelltext).

EJB(A) spricht mehrere ModifierBean an. Die Lock-Abschnitte sollen ja möglichst kurz sein und so wollte ich alles, was nicht zwingend gelockt werden muss auch nicht in diesem Scope abhandeln.

Nun ist das vergeben der Locks an sich kein Problem.
Nur werden sie beim Verlassen von ModifierBean nicht wieder aufgehoben, sondern ersz wenn ServiceBean verlassen wird.

Was ich bisher schon versucht habe
  1. in ServiceBean wird ModifierBean wird per @EJB vom Container injected
    in EJB(B) wird der Entitymanager per @PersistenceContext eingebunden
    so wird das Lock aber erst beim Verlassen von ServiceBean aufgegeben.
  2. wie oben, nur gibt es kein globales statisches ModifierBean, sondern es wird in jeder Servicemethode per ctx.lookup geholt. Dabei ist es ja in einem festen Scope zwischen den { } und somit ist die Lebenszeit begrenzt ... dachte ich -> weit gefehlt.
  3. wieder wie 1. diesmal aber mit einem Extended PersistenceContext und manuellem flush. Leider interessiert sich der EM nicht für das em.flush. Die Info landet wieder erst nach verlassen von ServiceBean in der DB.
  4. dann habe ich mal eine Session per @PersistenceContext injecten lassen ... wieder nix
  5. zum Schluss habe ich die HibernateFactory per JNDI geholt um so an eine Session zu kommen. Nur von dieser ging es dann nicht eine Transaktion zu eröffnen.
  6. ich habe auch mal ein @Transaction(REQUIRED) and ie Methode geschrieben, da das laut Hibernate den Context (Session/Transaktion) beim Verlassen der Methode beendet. Aber auch das schlug fehl.


So langsam gehen mir die Ideen aus, bzw. kommt die richtige nicht.

Was ich möchte ist,
dass das Lock beim Verlassen der ModifierBean-Methode wieder entfernt wird und nicht erst beim Verlassen von ServiceBean, sodass nur ModifierBean-Methoden einen gegenseitigen Ausschluss gewähren, ServiceBean-Methoden aber quasi-parallel ablaufen können.

Oder liegt es gar an JBoss, der evtl. den Context zu spät beendet?

Wenn jemand einen Hinweis hat, der mich weiterbringt, würde ich mich sehr freuen.
So, und nun der Quellcode von dem Beispiel



Start.java
Code:
package pz.test.writeDB;

import javax.naming.Context;
import javax.naming.InitialContext;

import pz.test.writeDB.service.IService;

public class Start extends Thread
{
   private static IService sb;
   private static Integer id;
   private String value;
   
   public Start(String value)
   {
      this.value = value;
   }
   
   public void run()
   {
      sb.modify(id, value);
      System.out.println("finished");
   }
   
   public static void main(String[] args)
   {
   //lookup service bean
      try
      {
         Context context = new InitialContext();
         sb = (IService) context.lookup("CServiceBean/remote");
      }
      catch (Exception e)
      {
         e.printStackTrace();
      }
      
   //create working object
      id = sb.create();
      
   //start 2 concurrent threads
      new Start("eins").start();
      new Start("zwei").start();
   }
}



CServiceBean.java
Code:
package pz.test.writeDB.service;

import javax.ejb.EJB;
import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.naming.InitialContext;

import pz.test.writeDB.IModifier;

@Stateless
@Remote(value=IService.class)
@Local(value=IService.class)
public class CServiceBean implements IService
{
//   @EJB
//   private static IModifier modifier;
   
   public void modify(Integer id, String value)
   {
      try
      {
         InitialContext initial = new InitialContext();
         IModifier modifier = (IModifier) initial.lookup("CModifierBean/remote");
         
         modifier.modify(id, value);   //breakpoint
         System.out.println("");
         modifier.flushEM();
      }
      catch(Exception e)
      {
         e.printStackTrace();
      }
      //warum existiert der Persistence Context hier immer noch?
      //das Lock ist noch nicht aufgehoben
      
      try
      {
         InitialContext initial = new InitialContext();
         IModifier modifier = (IModifier) initial.lookup("CModifierBean/remote");
         
         modifier.modify(id, value+"+");   //breakpoint
         System.out.println("");
         modifier.flushEM();
      }
      catch(Exception e)
      {
         e.printStackTrace();
      }
      //warum existiert der Persistence Context hier immer noch?
      //das Lock ist noch nicht aufgehoben
      
      System.out.println("modified");   //breakpoint
      
      //erst wenn die folgende Klammer verlassen wird
      //(repektive wenn wieder im Start-Objekt)
      //wird das Lock aufgegeben
   }
   
   /**
    * Testobjekt anlegen
    */
   public Integer create()
   {
      try
      {
         InitialContext initial = new InitialContext();
         IModifier modifier = (IModifier) initial.lookup("CModifierBean/remote");
         
         Integer id = modifier.create();
         return id;
      }
      catch(Exception e)
      {
         e.printStackTrace();
      }
      
      return null;
   }
}



CModifierBean.java
Code:
package pz.test.writeDB;

import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.persistence.PersistenceProperty;

import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.ejb.HibernateEntityManager;
import org.jboss.ejb3.entity.ExtendedEntityManager;

import pz.test.writeDB.model.TestObject;

@Stateless
@Remote(value=IModifier.class)
@Local(value=IModifier.class)
public class CModifierBean implements IModifier
{
   //das ist der Standard, aber so geht nicht, was ich will
   @PersistenceContext
   private EntityManager em;
   
   //noch ein paar gescheiterte Versuche mit statischen Members
   
//   @PersistenceContext
//   (
//      type=PersistenceContextType.EXTENDED,
//      properties = @PersistenceProperty(name="org.hibernate.flushMode", value="MANUAL")
//   )
//   private Session session;
   
   
//   @PersistenceContext (
//      type = PersistenceContextType.EXTENDED,
//      properties = @PersistenceProperty (
//         name="org.hibernate.flushMode",
//         value="MANUAL")
//   )
//   private EntityManager em;

   
//   private Session getSession()
//   {
//      try
//      {
//         Context ctx = new InitialContext();
//         SessionFactory factory = (SessionFactory) ctx.lookup("java:/hibernate/HibernateFactory");
//         return factory.openSession();
//      }
//      catch(Exception e)
//      {
//         e.printStackTrace();
//      }
//      return null;
//   }
   
   public Integer create()
   {
      TestObject to = new TestObject();
      to.setValue("init");
      
//      Session session = getSession();
//      Transaction tx = session.beginTransaction();
      
      to = em.merge(to);   //muss gecastet werden, wenn session verw. wird
      
//      tx.commit();
//      session.close();
      
      return to.getId();
   }
   
   @TransactionAttribute(TransactionAttributeType.REQUIRED)
   public void modify(Integer id, String value)
   {
//      Session session = getSession();
//      Transaction tx = session.beginTransaction();
      
      Session session = ((HibernateEntityManager) em).getSession();
      TestObject to = (TestObject) session.get(TestObject.class, id, LockMode.UPGRADE);
      
      to.setValue(value);
      session.merge(to);
      
//      tx.commit();
      session.close();
      
      System.out.println("merged");
   }
   
   /**
    * lock entity
    */
   public <T> T lock(T entity)
   {
      //normaler PC
//      Session session = ((HibernateEntityManager) em).getSession();
      
      //wenn extended PC
//      Session session = ((ExtendedEntityManager) em).getHibernateSession();
//      
//      
////   T t = null;
//      try
//      {
////      T result = (T) session.get(entity.getClass(), 1, LockMode.UPGRADE);
////      session.refresh(entity, LockMode.UPGRADE);
////      t = result;
//      }
//      catch(ClassCastException e)
//      {
//         e.printStackTrace();
//      }
////   return t;
      
      return entity;
   }
   
   @Remove
   public void flushEM()
   {
//      session.flush();
//      session.close();
//      em.flush();
//      em.close();
   }
}



persistence.xml
Code:
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
   version="1.0">
   <persistence-unit name="dbtest">
      <jta-data-source>
         java:/dbtest
      </jta-data-source>
      <properties>
            <property name="hibernate.connection.isolation" value= "8" />
            <property name="hibernate.dialect" value= "org.hibernate.dialect.MySQLInnoDBDialect" />
            <property name="hibernate.hbm2ddl.auto" value="update" />
            <property name="hibernate.session_factory_name" value="java:/hibernate/HibernateFactory"/>
            <!--
            <property name="hibernate.jndi.class">org.jnp.interfaces.NamingContextFactory</property>
            <property name="hibernate.jndi.url">t3://127.0.0.1:1099</property>
             -->
      </properties>
   </persistence-unit>
</persistence>


jndi.properties
Code:
java.naming.provider.url=jnp://127.0.0.1:1099
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
jnp.socket.Factory=org.jnp.interfaces.TimedSocketFactory


Top
 Profile  
 
 Post subject: Lösung
PostPosted: Mon Jan 12, 2009 6:28 am 
Newbie

Joined: Fri Jan 09, 2009 3:04 pm
Posts: 2
Die Lösung scheint zu sein eine neue Transaktion per Annotation zu erzwingen. Dann ist der Context wirklich am Ende der modify-Methode beendet und erstreckt sich nicht auch noch in den Service ...

Hier noch der Codeschnipsel, der mich an Ziel brachte.

Code:
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void modify(Integer id, String value)
{
   ...
}


Meine Annahme war ja, das der default-Wert (also TransactionAttributeType.REQUIRED) die Transaktion am Ende der Methode schließt. Transaktion weg -> Lock weg.
Hibernate hebt aber wohl solche Änderungen ne Weile auf um zu sehen, ob man nicht doch noch was an den Daten ändern will, um so Performanz zu gewinnen.

So, ich hoffe meine Unwissenheit und Lösung kann noch anderen Entwicklern helfen, die evtl. auf ein ähnliches Problem stoßen.

bye
j-guy


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 2 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.