【问题标题】:Allow EntityManagerFactory injection to be null when database service is off数据库服务关闭时允许 EntityManagerFactory 注入为空
【发布时间】:2015-09-30 05:50:43
【问题描述】:

我有一个 Jersey Web 服务,它使用 Hibernate 来做一些持久性。我已经使用 hk2 提供程序逻辑实现了 EntityManagerFactory 的创建/处置,我发现堆栈溢出,这极大地有助于保持数据库连接的数量较低。我不想强迫用户拥有一个数据库,所以我希望代码能够优雅地处理这种情况。但是,除了必须注释掉我的 @Inject 注释之外,我似乎无法弄清楚。任何人都知道如何使用自定义 @Inject 并对其进行编码以使其为空?

当 Persistence.create 失败时,我试图在 DBManager 中捕获该异常,并在我的 WebServiceClass 中检查 null。但它在@Inject 行崩溃并且没有捕获到异常。我环顾了那个 findOrCreate null 异常,发现有一个名为 supportsNullCreation() 的方法,但没有找到有关如何使用它的示例。

这是我的代码的样子:

使用 HK2 的可注入数据库提供程序:

public class DbManager 
        implements Factory<EntityManagerFactory>
{
   private static EntityManagerFactory factory = null;

   @Inject
   public DbManager()
   {
      try
      {
         factory = Persistence.createEntityManagerFactory("myapp");
      }
      catch ( Exception eee )
      {
         // just means DB is not connected which I want to allow
         System.out.println("No DB, that should be okay");
      }
   }

   @Override
   public EntityManagerFactory provide() {
      return factory;
   }

   @Override
   public void dispose(EntityManagerFactory emf) {
      if ( emf != null && emf.isOpen() )
      {
         emf.close();
      }
   }

   public EntityManagerFactory getEntityMgrFactory()
   {
      return factory;
   }
}

然后是在 Jersey 应用程序中创建提供程序的方式:

@ApplicationPath("rest")
public class MyApplication extends ResourceConfig 
{
    public MyApplication()
    {

        ....

    // Provider of DB
    this.register( new AbstractBinder()
    {
       @Override
       public void configure()
       {
 bindFactory(DbManager.class).to(EntityManagerFactory.class).in(Singleton.class);
       }
    });
}

那么就是这样使用的:

@Singleton
@Path("myservice")
public class WebServiceClass
{

   // NOTE: Right now I have to comment this to run without a DB
   @Inject
   private EntityManagerFactory entityManagerFactory = null;
   ...

我得到的例外是这个......

java.lang.IllegalStateException: Context 
 org.jvnet.hk2.internal.SingletonContext@6cae5847 findOrCreate returned a null for 
descriptor SystemDescriptor(
    implementation=com.db.DbManager
    contracts={javax.persistence.EntityManagerFactory}
    scope=javax.inject.Singleton
    qualifiers={}
    descriptorType=PROVIDE_METHOD
    descriptorVisibility=NORMAL
    metadata=
    rank=0
    loader=org.glassfish.hk2.utilities.binding.AbstractBinder$2@7050f2b1
    proxiable=null
    proxyForSameScope=null
    analysisName=null
    id=145
    locatorId=0
    identityHashCode=863132354
    reified=true)
    at org.jvnet.hk2.internal.Utilities.createService(Utilities.java:2075)
...

【问题讨论】:

  • 那么在你的例子中,你是说如果 EMF 创建失败,你仍然希望它正常工作,这意味着注入将只是空的?这样做有什么好处?我理解正确吗?
  • 我想问题是如果没有数据库,用户将如何与服务交互?对端点的调用返回什么?
  • 是的,我希望它在 EMF 创建失败时正常工作。
  • 对端点的调用会向用户返回一些数据,这些数据可能会或可能不会持久化,具体取决于数据库是否已安装和运行。

标签: hibernate jpa dependency-injection jersey jax-rs


【解决方案1】:

您想要的是动态注入 EMF 依赖项。通常所有的注入都是静态的,这意味着当依赖它们的对象被创建时,所有的依赖都是连接的。

使用任何依赖注入机制(不仅是 HK2)的简单解决方案是创建一个包装器对象,该对象将保存对真实依赖项的引用,如果依赖项不可用,则为 null。同样可以通过将 EntityManagerFactory 包装到一个或零个元素的集合中来实现。它可能看起来像这样:

// factory wrapper
public class EMFHolder {
   private EntityManagerFactory emf;
   public EMFHolder(EntityManagerFactory emf) { this.emf = emf;}
   public EntityManagerFactory getEmf() { return this.emf; 
}

// provider
public class DbManager implements Factory<EntityManagerFactory> {
 // ... 
   @Override
   public EMFHolder provide() {
      return new EMFHolder(factory);
   }
 // ...
}

// using factory if not null
public class WebServiceClass
{
   @Inject
   private EMFHolder emfHolder;

   public void doComethingWithEMF() {
     if (emfHolder.getEmf() != null) {
        // do something with the factory...
     }
   }
}

还可以查看 HK2 Iterable Provider,但我认为它不允许您从 provide 方法返回空值。

【讨论】:

    【解决方案2】:

    你可以看看Null Object pattern

    使用该模式,如果不存在数据库,您可以在配置中注册您的 Null Object 实现 Factory&lt;EntityManagerFactory&gt; 而不是 DbManager

    【讨论】:

    • 我可以使用更多细节。我尝试创建一个 NullableDbMgr,但是 provide() 方法应该为 EntityManagerFactory 返回 null 对吗?如果是这样,它会导致同样的问题。你是说我应该提供一些不同的东西吗?如果是这样,这不会破坏提供者的有效性吗?
    • 是的,您还应该创建EntityManagerFactoryNull Object 实现,以便在没有数据库时使用它。这应该警告您,如果EntityManagerFactory 不是我自己的接口,为什么我直接在我的消费类中使用它?除此之外,为了解决您的问题,您还可以考虑使用内存数据库。
    • 使用 null 对象的想法是,您不会从 provide 方法返回 null,但您将创建自己的 EntityManagerFactory 接口实现,使用空方法(例如,可能会引发运行时异常)。在客户端代码中,您将尝试检查是否收到了此类空对象。如果是,则表示没有与数据库的连接。否则,您将照常使用注入的 EMF 对象。
    猜你喜欢
    • 1970-01-01
    • 2020-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-16
    • 1970-01-01
    • 2019-09-01
    • 2022-06-19
    相关资源
    最近更新 更多