0

我正在使用 EJB 计时器,但是在尝试在同一个项目中同时运行计时器和持久实体时遇到了麻烦。在我的初始设置中,我只有计时器,并且这些计时器按预期触发:

@Stateless
public class TimerHandler {

    @Resource
    protected TimerService mTimerService;

    @PostConstruct
    public void init() {
        // could do cool stuff but choose not to
    }

    public Timer start(long aDuration) {
        TimerConfig conf = new TimerConfig();
        conf.setPersistent(false); // don't want the timer to be saved
        return mTimerService.createSingleActionTimer(aDuration, conf);
    }

    @Timeout
    public void timeOutAction(Timer aTimer) {
        // does fancy stuff
        System.out.println("So fancy :)");
    }

}

我在让计时器运行时遇到了一些麻烦,但我采用了蛮力的方式并重新安装了 Payara (Glassfish)。在此之后使用计时器很好。我可以这样开始和取消它:

@Stateful
public class MyClass {

    @EJB
    private TimerHandler mTimerHandler;

    private Timer mTimer;

    public void startTimer(int aDuration) {
        mTimer = mTimerHandler.start(aDuration);
    }

    public void stopTimer() {
        try {
            mTimer.cancel();
        } catch (NoSuchObjectLocalException | NullPointerException ex) {
            System.out.println("There is no timer running.");
        }
    }
}

但是,在我尝试将实体添加到我的项目后,问题就出现了。我的实体如下所示:

@Entity
public class TestEntity implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String testValue;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTestValue() {
        return testValue;
    }

    public void setTestValue(String value) {
        testValue = value;
    }

    // removed standard code for @Override of equals(), 
    // hashCode() & toString()
}

我通过我的控制器 bean 操作:

@Stateless
public class TestDBController {
    @PersistenceContext(unitName = "TimerTestWithDBPU")
    private EntityManager em;

    public long saveValue(String value) {
        TestEntity entity = new TestEntity();
        entity.setTestValue(value);
        em.persist(entity);
        em.flush();
        return entity.getId();
    }

    public String getValue(long aId) {
        TestEntity entity = em.find(TestEntity.class, aId);
        return entity.getTestValue();
    }
}

并且我通过以下方式设置了我的持久性单元(persistence.xml):

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" 
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
            xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence 
            http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
  <persistence-unit name="TimerTestWithDBPU" transaction-type="JTA">
    <jta-data-source>jdbc/timer_test_pool</jta-data-source>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
      <property name="javax.persistence.schema-generation.database.action" 
                  value="create"/>
    </properties>
  </persistence-unit>
</persistence>

添加此实体和持久性单元后,我收到以下错误:

EJB Timer Service is not available. 
            Timers for application with id [XYZ] will not be deleted

为什么是这样?您不能同时使用 ejb 计时器和持久实体运行应用程序吗?

4

1 回答 1

0

事实证明,你可以!谁能想到...

Glassfish 应用开发指南中的这句话为我指明了正确的方向。

使用 EJB Timer Service 等同于与单个 JDBC 资源管理器进行交互。如果 EJB 组件或应用程序通过 JDBC 直接或间接(例如,通过实体 bean 的持久性机制)访问数据库,并且还与 EJB Timer Service 交互,则必须使用 XA JDBC 驱动程序配置其数据源。

我将尝试描述我所做的(我是 Java EE 领域的新手,所以某些概念和功能名称可能不正确)

事实证明,在我的情况下,您需要在应用程序服务器 (Payara) 中配置资源,以便它使用 XA JDBC 驱动程序而不是普通的 JDBC 驱动程序。(我不完全明白为什么,如果有人愿意详细说明,我很想听听)

去做这个:

  1. 创建一个数据库,给它一个名称并指定用户名密码
  2. 进入 Payaras 管理页面 -> 资源 -> JDBC -> JDBC 连接池 -> 新建
  3. 指定:
    • 一个名字,在我的例子中是“TestPool”
    • 选择资源类型为:“javax.sql.XADataSource”
    • 选择您的供应商:我使用 JavaDB
  4. 点击下一步,让 Datasource Classname 为默认值
  5. 至少指定(我相信)以下属性:
    • 服务器名称(例如本地主机)
    • 端口号(例如 1527)
    • 密码(例如测试)
    • 用户(例如测试)
    • URL(例如 jdbc:derby://localhost:1527/Test)
    • 数据库名称(例如测试)
  6. 点击完成

ConnectionPool 需要连接到 JDBC 资源并获取 JNDI 名称,以便服务器能够使用它。在 Payara 的管理页面中:

  1. 转到 -> 资源 -> JDBC -> JDBC 资源 - 新建
  2. 给它一个名字(例如 jdbc/TestPool)并选择你之前创建的池。
  3. 如果您愿意,请添加描述(描述很酷)
  4. 点击确定!

现在将新的连接池添加为持久单元 (persistance.xml) 中项目的数据源。

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="htt...>
  <persistence-unit name="TimerTestPU" transaction-type="JTA">
    <jta-data-source>jdbc/TestPool</jta-data-source> <!-- Magic Line -->
    .
    .
    .
  </persistence-unit>
</persistence>

在这一点上,我得到了它的工作。但是似乎仍然存在问题,因为如果我运行其他使用计时器的应用程序,同时仍然为数据库使用非 XA JBDC 驱动程序,它实际上破坏了我开始工作的内容。这表现为无法 Ping 默认的 __TimerPool 连接池,并出现以下错误(如果有人能对此有所了解,我会全力以赴):

java.lang.NoClassDefFoundError: Could not initialize class
        org.apache.derby.jdbc.EmbeddedDriver Could not initialize class 
        org.apache.derby.jdbc.EmbeddedDriver

我最终删除了 ejb 驱动程序 (__TimerPool) 的默认连接池,并使用与上述相同的过程 (1-10) 创建了一个新连接池,但名称类似于“TimerEJB”。为了让它正常工作,您需要“安装”数据库,这意味着创建一个适合应用程序服务器的标准数据库。Payara 和 Glassfish 为此提供了 SQL:

%install-path-of-payara%\glassfish\lib\install\databases\

选择文件 ejbtimer_[VENDOR_NAME].sql 并在您创建的数据库上运行它。在此之后,您应该将此设置为 ejb 计时器的默认连接工具。在 Payaras 管理页面:

  1. 转到 -> 配置 -> 服务器配置 -> EJB 容器 -> EJB 计时器服务
  2. 输入您为 TimerEJB 连接池(第 8 步)创建的 JNDI 名称(资源名称)作为“计时器数据源”
  3. 节省

现在重新启动一切,或者至少重新启动 Payara 和数据库服务器,您应该一切顺利。

最后一部分的灵感来自@ejohansson对相关问题的回答。

希望这可以帮助某人:)

于 2017-01-20T18:19:18.290 回答