当数据库服务关闭时允许 EntityManagerFactory 注入为 null

Allow EntityManagerFactory injection to be null when database service is off

我有一个 Jersey Web 服务,它使用 Hibernate 来做一些持久性。我已经使用 hk2 提供程序逻辑实现了 EntityManagerFactory 的 creation/disposal 我发现了堆栈溢出,这极大地帮助保持了较低的数据库连接数。我不想强迫用户拥有数据库,所以我希望代码能够优雅地处理这种情况。但是,除了必须注释掉我的 @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@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)
...

你可以看看Null Object pattern

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

你想要的是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 方法中 return 空值。