Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何用EJB3.0实现一个合适的计数器bean?_Java_Jpa_Concurrency_Jakarta Ee_Ejb 3.0 - Fatal编程技术网

Java 如何用EJB3.0实现一个合适的计数器bean?

Java 如何用EJB3.0实现一个合适的计数器bean?,java,jpa,concurrency,jakarta-ee,ejb-3.0,Java,Jpa,Concurrency,Jakarta Ee,Ejb 3.0,[编辑]这个问题是“如何使用EJB3和JPA2.0对实体bean进行原子更改”。应该很简单,对吗 我试图根据目前为止得到的答案来修改代码。我正在使用JBoss 6.0.0M2和高超音速(只需下载并调用run.bat) 我的测试用例:创建3个线程,并在一个循环中调用其中一个testCounterMitLock*()500次。因此,成功的测试应该打印“Anzahl eingetragene Zeilen:1500”(3*500) 我试过: CounterTestVersion ct

[编辑]这个问题是“如何使用EJB3和JPA2.0对实体bean进行原子更改”。应该很简单,对吗

我试图根据目前为止得到的答案来修改代码。我正在使用JBoss 6.0.0M2和高超音速(只需下载并调用run.bat)

我的测试用例:创建3个线程,并在一个循环中调用其中一个
testCounterMitLock*()
500次。因此,成功的测试应该打印“Anzahl eingetragene Zeilen:1500”(3*500)

我试过:

        CounterTestVersion ct = manager.find(CounterTestVersion.class, 1);
        manager.lock(ct, LockModeType.WRITE);
        int wert = ct.getWert();
显然不起作用,因为不同的线程可以在应用锁之前更改数据库中的值。所以我试图解决这个问题:

        CounterTestVersion ct = manager.find(CounterTestVersion.class, 1);
        manager.lock(ct, LockModeType.WRITE);
        manager.refresh (ct);
        int wert = ct.getWert();
refresh()
应该给我当前值,隐式查询也应该确保对象现在被锁定。没有这样的运气。让我们试试JPA2.0:

        CounterTestVersion ct = manager.find(CounterTestVersion.class, 1, LockModeType.WRITE);
        int wert = ct.getWert();
那也不行。也许锁不够用

        CounterTestVersion ct = manager.find(CounterTestVersion.class, 1, LockModeType.PESSIMISTIC_WRITE);
        int wert = ct.getWert();
嗯。。。也不行!最后一次绝望的尝试:

        CounterTestVersion ct = manager.find(CounterTestVersion.class, 1, LockModeType.PESSIMISTIC_WRITE);
        manager.flush();
        manager.refresh (ct);
        int wert = ct.getWert();
好的。。。有人能解释为什么什么都不管用吗?我没有主意了

[EDIT2]PS:雪上加霜的是,这是最后一个运行线程的最后一个输出:

commit/rollback: 441/62
(441+62=503)

这是完整的代码。首先是bean:

package server.kap15;

import java.rmi.RemoteException;

import javax.ejb.*;
import javax.persistence.*;

@Stateful
public class CounterTestBean implements CounterTestRemote, SessionSynchronization {
    @PersistenceContext(unitName = "JavaEE")
    EntityManager manager;

    private int commit = 0;

    private int rollback = 0;

    public void initDatenbank() {
        manager.createNamedQuery("CounterTest.deleteAll").executeUpdate();
        manager.createNamedQuery("TestTabelle.deleteAll").executeUpdate();
        CounterTestVersion ct = new CounterTestVersion();
        ct.setNr(1);
        ct.setVersion(1);
        ct.setWert(1);
        manager.persist(ct);
    }

    public boolean testCounterOhneLock() {
        try {
            CounterTest ct = manager.find(CounterTest.class, 1);
            int wert = ct.getWert();
            ct.setWert(wert + 1);
            TestTabelle tt = new TestTabelle();
            tt.setNr(wert);
            manager.persist(tt);
            manager.flush();
            return true;
        } catch (Throwable t) {
            return false;
        }
    }

    public boolean testCounterMitLock() {
        try {
            CounterTestVersion ct = manager.find(CounterTestVersion.class, 1);
            manager.lock(ct, LockModeType.WRITE);
            int wert = ct.getWert();
            ct.setWert(wert + 1);
            TestTabelle tt = new TestTabelle();
            tt.setNr(wert);
            manager.persist(tt);
            manager.flush();
            return true;
        } catch (Throwable t) {
            return false;
        }
    }

    public boolean testCounterMitLock2() {
        try {
            CounterTestVersion ct = manager.find(CounterTestVersion.class, 1);
            manager.lock(ct, LockModeType.WRITE);
            manager.refresh (ct);
            int wert = ct.getWert();
            ct.setWert(wert + 1);
            TestTabelle tt = new TestTabelle();
            tt.setNr(wert);
            manager.persist(tt);
            manager.flush();
            return true;
        } catch (Throwable t) {
            return false;
        }
    }

    public boolean testCounterMitLock3() {
        try {
            CounterTestVersion ct = manager.find(CounterTestVersion.class, 1, LockModeType.WRITE);
            int wert = ct.getWert();
            ct.setWert(wert + 1);
            TestTabelle tt = new TestTabelle();
            tt.setNr(wert);
            manager.persist(tt);
            manager.flush();
            return true;
        } catch (Throwable t) {
            return false;
        }
    }

    public boolean testCounterMitLock4() {
        try {
            CounterTestVersion ct = manager.find(CounterTestVersion.class, 1, LockModeType.PESSIMISTIC_WRITE);
            int wert = ct.getWert();
            ct.setWert(wert + 1);
            TestTabelle tt = new TestTabelle();
            tt.setNr(wert);
            manager.persist(tt);
            manager.flush();
            return true;
        } catch (Throwable t) {
            return false;
        }
    }

    public boolean testCounterMitLock5() {
        try {
            CounterTestVersion ct = manager.find(CounterTestVersion.class, 1, LockModeType.PESSIMISTIC_WRITE);
            manager.flush();
            manager.refresh (ct);
            int wert = ct.getWert();
            ct.setWert(wert + 1);
            TestTabelle tt = new TestTabelle();
            tt.setNr(wert);
            manager.persist(tt);
            manager.flush();
            return true;
        } catch (Throwable t) {
            return false;
        }
    }

    public boolean testCounterMitVersion() {
        try {
            CounterTestVersion ctv = manager.find(CounterTestVersion.class, 1);
            int wert = ctv.getWert();
            ctv.setWert(wert + 1);
            manager.flush();
            TestTabelle tt = new TestTabelle();
            tt.setNr(wert);
            manager.persist(tt);
            manager.flush();
            return true;
        } catch (OptimisticLockException e) {
            System.out.println(">>> Versionskonflikt !");
            return false;
        } catch (Throwable t) {
            System.out.println(t.getMessage());
            return false;
        }
    }

    public long anzTestZeilen() {
        Query query = manager.createNamedQuery("TestTabelle.anzZeilen");
        Long anzahl = (Long) query.getSingleResult();
        return anzahl;
    }

    public void afterBegin() throws EJBException, RemoteException {
    }

    public void beforeCompletion() throws EJBException, RemoteException {
    }

    public void afterCompletion(boolean committed) throws EJBException,
    RemoteException {
        if (committed)
            commit++;
        else
            rollback++;
        System.out.println("commit/rollback: " + commit + "/" + rollback);
    }
}
远程接口:

package server.kap15;

import javax.ejb.Remote;

@Remote
public interface CounterTestRemote {
    public void initDatenbank();

    public boolean testCounterOhneLock();

    public boolean testCounterMitLock();
    public boolean testCounterMitLock2();
    public boolean testCounterMitLock3();
    public boolean testCounterMitLock4();
    public boolean testCounterMitLock5();

    public boolean testCounterMitVersion();

    public long anzTestZeilen();
}
persistence.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<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="JavaEE">
        <jta-data-source>java:DefaultDS</jta-data-source>
    </persistence-unit>
</persistence>

java:DefaultDS
测试客户端:

package client.kap15;

import java.util.Properties;
import javax.naming.*;
import javax.rmi.PortableRemoteObject;
import server.kap15.CounterTestRemote;

public class CounterTestMitLock extends Thread {
    CounterTestRemote ctr;

    public static void main(String[] args) {
        try
        {
            testMitLock();
            testMitLock2();
            testMitLock3();
            testMitLock4();
            testMitLock5();
        }
        catch (Exception e)
        {
            e.printStackTrace ();
        }
    }

    static int N = 3;
    static CounterThread[] ct = new CounterThread[N];

    private static void testMitLock () throws InterruptedException
    {
        System.out.println("--- Counter Test MIT Lock ----------------------");
        System.out.println("Testinstanzen erzeugen...");
        for (int i=0; i<N; i++)
            ct[i] = new CounterThreadMitLock();

        runTest ();
    }

    private static void testMitLock2 () throws InterruptedException
    {
        System.out.println("--- Counter Test MIT Lock2 ----------------------");
        System.out.println("Testinstanzen erzeugen...");
        for (int i=0; i<N; i++)
            ct[i] = new CounterThreadMitLock2();

        runTest ();
    }

    private static void testMitLock3 () throws InterruptedException
    {
        System.out.println("--- Counter Test MIT Lock3 ----------------------");
        System.out.println("Testinstanzen erzeugen...");
        for (int i=0; i<N; i++)
            ct[i] = new CounterThreadMitLock3();

        runTest ();
    }

    private static void testMitLock4 () throws InterruptedException
    {
        System.out.println("--- Counter Test MIT Lock4 ----------------------");
        System.out.println("Testinstanzen erzeugen...");
        for (int i=0; i<N; i++)
            ct[i] = new CounterThreadMitLock4();

        runTest ();
    }

    private static void testMitLock5 () throws InterruptedException
    {
        System.out.println("--- Counter Test MIT Lock5 ----------------------");
        System.out.println("Testinstanzen erzeugen...");
        for (int i=0; i<N; i++)
            ct[i] = new CounterThreadMitLock5();

        runTest ();
    }

    private static void runTest () throws InterruptedException
    {
        System.out.println("Datenbank initialisieren...");
        ct[0].ctr.initDatenbank();

        System.out.println("Test durchführen...");
        for (int i=0; i<N; i++)
            ct[i].start();

        System.out.println("Auf Ende warten...");
        for (int i=0; i<N; i++)
            ct[i].join();

        System.out.println("Anzahl eingetragene Zeilen: " + ct[0].ctr.anzTestZeilen());
    }

    private static CounterTestRemote verbinden() {
        try {
            Properties p = new Properties();
            p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
            p.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
            p.put(Context.PROVIDER_URL, "jnp://localhost:1099");
            Context ctx = new InitialContext(p);

            Object ref = ctx.lookup("CounterTestBean/remote");
            CounterTestRemote ctr = (CounterTestRemote) PortableRemoteObject.narrow(ref, CounterTestRemote.class);

            return ctr;
        } catch (NamingException e) {
            System.out.println("ERROR - NamingException!");
            System.exit(-1);
        }
        return null;
    }

    public abstract static class CounterThread extends Thread
    {
        protected CounterTestRemote ctr;

        public CounterThread ()
        {
            this.ctr = verbinden ();
        }

        public void run() {
            for (int i = 0; i < 500; i++)
                test ();
        }

        public abstract void test ();
    }

    public static class CounterThreadMitLock extends CounterThread
    {
        @Override
        public void test ()
        {
            this.ctr.testCounterMitLock();
        }

    }

    public static class CounterThreadMitLock2 extends CounterThread
    {
        @Override
        public void test ()
        {
            this.ctr.testCounterMitLock2();
        }

    }

    public static class CounterThreadMitLock3 extends CounterThread
    {
        @Override
        public void test ()
        {
            this.ctr.testCounterMitLock3();
        }

    }

    public static class CounterThreadMitLock4 extends CounterThread
    {
        @Override
        public void test ()
        {
            this.ctr.testCounterMitLock4();
        }

    }

    public static class CounterThreadMitLock5 extends CounterThread
    {
        @Override
        public void test ()
        {
            this.ctr.testCounterMitLock5();
        }

    }
}
package client.kap15;
导入java.util.Properties;
导入javax.naming.*;
导入javax.rmi.PortableRemoteObject;
导入server.kap15.CounterTestRemote;
公共类CounterTestMitLock扩展线程{
反测试远程ctr;
公共静态void main(字符串[]args){
尝试
{
testMitLock();
testMitLock2();
testMitLock3();
testMitLock4();
testMitLock5();
}
捕获(例外e)
{
e、 printStackTrace();
}
}
静态int N=3;
静态反螺纹[]ct=新反螺纹[N];
私有静态void testMitLock()抛出InterruptedException
{
System.out.println(“----计数器测试MIT锁---------------------”);
System.out.println(“Testinstanzen-erzeugen…”);

对于(int i=0;i您没有显示如何处理testCounterWithLock的返回值。我的猜测是您得到了乐观的锁定失败,并且返回值有时为false

当冲突在实践中可能很少发生时,Optimatic锁定是一种合理的模式,调用方可以合理地重做工作。因此,如果您遇到乐观的失败,您可以重试

或者,使用悲观锁定模型,这将在您读取的点锁定数据库中的行。您可以这样做,我将悲观锁定模式添加到对find()的调用中。使用悲观锁定需要小心,因为这很容易导致错误的并发性和/或死锁。

我有几点意见:

  • 您当前正在使用乐观锁定,但我在您的实体上没有看到任何
    @Version
    字段。我认为这不可行
  • 如果您希望计数器增加1500倍,不要使用乐观锁定(您不希望任何更新被拒绝并出现
    OptimisticLockingException
    )而使用悲观锁定
  • 吞咽扔掉的东西确实是错误的,你想让容器完成他的工作(但我想你知道这一点)
所以,这里我会用这个来代替:

manager.lock(ct, LockModeType.READ);
然后卸下
锁扣(可丢弃的t)

更新:我现在无法测试,但我会使用类似的方法(其余代码不变):

我真的怀疑这是否有效。首先,读锁不会阻止其他线程更新该行。其次,另一个线程可以更新find()和getCounter()之间的行

你是对的,我走得太快了,上面的肯定不是一个解决方案,@ewernli也是对的,JPA1.0不支持悲观锁定策略,你必须依赖数据库来实现这一点(并使用
SELECT for UPDATE
语义).不知怎的,我忘记了这一点,对阅读模式造成了很大的混乱。我的错。谢谢你指出这一点


我认为您必须使用LockModeType.WRITE,但也许可以在lock()之后使用em.refresh()来确保实体不过时

使用
LockModeType.WRITE
时,在
UPDATE
WHERE
子句中添加用
@Version
注释的实体字段,并在
更新期间执行并发检查:

UPDATE COUNTERTEST SET COUNTER = ?, OPT_LOCK = ? 
WHERE ((ID = ?) AND (OPT_LOCK = ?))
如果
WHERE
子句未能匹配记录(因为另一个线程已经更新了实体),则持久性提供程序将抛出
OptimisticLockException

换句话说,在
lock()
之后刷新实体不会改变任何东西,另一个线程仍然可以在另一个线程修改计数器时刷新同一实体。以自动方式处理乐观锁定的唯一方法是实现重试机制

但是当
flush()
抛出a时(和的实例除外),当前事务被标记为回滚,因此不能用于事务目的。因此,每次重试都必须使用新事务执行。在无状态bean中,可以进行递归远程调用,但我认为这在有状态bean中没有意义,因此必须从客户端处理


最后,这并不是很令人满意,在我看来,JPA1.0中处理这个问题的不那么糟糕的方法是使用SELECT FOR UPDATE获得锁。

即使使用
LockModeType.READ
LockModeType.WRITE
,JPA1.0也只支持乐观锁。锁的获取仍然可以推迟到comm是时候了,所以你遇到了问题

发件人:

PA 1.0仅支持乐观读取 或
UPDATE COUNTERTEST SET COUNTER = ?, OPT_LOCK = ? 
WHERE ((ID = ?) AND (OPT_LOCK = ?))
Account acc = em.find( Account.class, id, PESSIMISTIC );
Query query = em.createNativeQuery("SELECT * ... FOR UPDATE"); // works with most db
Account acc = (Account) query.getSingleResult();
        CounterTestVersion ct = manager.find(CounterTestVersion.class, 1, LockModeType.PESSIMISTIC_WRITE);
        int wert = ct.getWert();
    Query query = manager.createNativeQuery ("select * from COUNTER_TEST where NR = 1 for update", CounterTestVersion.class);
    CounterTestVersion ct = (CounterTestVersion)query.getSingleResult ();
    int wert = ct.getWert ()+1;
ORA-00054: resource busy and acquire with NOWAIT specified