我可以通过在运行时指定数据库来使用JPA吗?

我可以通过在运行时指定数据库来使用JPA吗?,jpa,jakarta-ee,jdbc,jndi,Jpa,Jakarta Ee,Jdbc,Jndi,我在生产服务器上使用MySQL,每个客户都有自己的数据库和自己的数据库用户连接到自己的数据库。每个数据库都有完全相同的结构。客户(因此数据库)是在服务器的生命周期内添加的 目前,我使用类java.sql.DriverManager建立了一个MySQL数据库连接,该类工作正常。数据库url、用户、密码和驱动程序名称存储在多个属性文件中。servlet从当前客户读取属性文件,并使用java.sql.DriverManager创建java.sql.Connection 我真的很想使用JPA,但据我所知

我在生产服务器上使用MySQL,每个客户都有自己的数据库和自己的数据库用户连接到自己的数据库。每个数据库都有完全相同的结构。客户(因此数据库)是在服务器的生命周期内添加的

目前,我使用类
java.sql.DriverManager
建立了一个MySQL数据库连接,该类工作正常。数据库url、用户、密码和驱动程序名称存储在多个属性文件中。servlet从当前客户读取属性文件,并使用
java.sql.DriverManager
创建
java.sql.Connection

我真的很想使用JPA,但据我所知,JPA只能通过在webcontainer(在我的例子中是Tomcat)的配置中显式指定数据库来使用。但这意味着,当新客户在我的平台上注册时,我必须将新数据库添加到webcontainer配置中,然后重新启动webcontainer,这听起来不是一个好主意


有没有一种方法可以使用JPA,并且在webcontainer的生命周期内仍然可以灵活地添加新数据库?

不确定这对您的特定情况是否有帮助,但我设置了一个环境,其中hibernate.cfg.xml中指定的JDBC连接URL


jdbc:ucanaccess://C:/Users/Public/UCanHibernate.accdb;newDatabaseVersion=V2010
可以使用如下Java系统属性覆盖:

StandardServiceRegistryBuilder ssrb=新的StandardServiceRegistryBuilder()
.configure();//从hibernate.cfg.xml配置设置
//允许测试人员指定自己的连接URL(通过-D JVM参数)
字符串runtimeUrl=System.getProperty(“HIBERNATE\u CONNECTION\u URL”);
if(runtimeUrl!=null){
ssrb.applySetting(“hibernate.connection.url”,runtimeUrl);
}

您可能会从其他来源获得覆盖信息,但该原则可能仍然适用。

您完全可以按自己的要求执行,但恶魔在细节中

我称之为JPA Multitenance,这是一篇由随机搜索提供的有趣文章,它提出了一种使用CDI的有效方法。考虑到您还需要在运行时添加新租户这一事实,我将从一个更通用的方法开始

还请注意,Hibernate提供了多租户。我不知道您使用的是哪个JPA提供商,但我猜其他人也有类似的功能

基础 必须使用单独的、特定于租户的
EntityManagerFactory
实例来完成。确切的数据库连接URL可以通过传递到
Persistence.createEntityManagerFactory()
的映射给出。例如,假设存在
META-INF/persistence.xml
,则以下代码适用:

HashMap props = new HashMap();
props.put("javax.persistence.jdbc.url", /* tenant-specific JDBC URL*/);
EntityManagerFactory tenantSpecificEntityManagerFactory =
    Persistence.createEntityManagerFactory("name-of-persistence-unit-from-persistence.xml", props);
props
通常覆盖
persistence.xml
中指定的任何属性。可能只覆盖JDBCURL就足够了,可能还需要覆盖用户/密码或其他内容。从那时起,您可以获得特定于租户的持久性上下文并使用它:

EntityManager tenantSpecificEm = tenantSpecificEntityManagerFactory.createEntityManager();
陷阱:效率 每次需要特定于租户的
EntityManager
(然后关闭工厂)时,您都可以执行上述代码。但由于以下原因,这将是非常低效的:

  • 使用每个请求重新创建
    EntityManagerFactory
    很慢
  • 使用每个请求重新创建数据库连接更慢
  • 要解决这些问题,您需要:

  • 缓存
    EntityManagerFactory
    实例
  • 以某种方式使用连接池
  • 缓存
    EntityManagerFactory
    实例 我假设有一个健壮的机制将每个请求与适当的租户联系起来。此外,您还需要将租户名称的应用程序范围映射到相应的
    EntityManagerFactory
    实例。存储和使用的方式取决于应用程序,例如,是否有依赖项注入框架?第一个链接有一个使用CDI的解决方案,类似的解决方案将适用于其他DI容器

    连接池 应用服务器提供数据库连接池。雄猫也是。应用程序服务器可能允许您在运行时添加DB连接池,而无需重新启动服务器。我不知道您使用的Tomcat版本是否支持它(我想不支持,但请纠正我)。因此:

    • 如果应用服务器(在本例中为Tomcat)支持创建运行时连接池,请执行此操作并调整
      道具以使用它
    • 否则,您必须使用适用于每个
      EntityManagerFactory
      的自定义连接池。Hibernate至少似乎有这个
    保留设置
    我想您已经了解了这一点,但是运行时应用的设置(租户、租户名称到连接属性的映射)必须以某种方式持久化,以便在服务器重新启动时重新应用它们。这可能是一个配置文件或另一个“管理”数据库。

    与您的问题无关,但您不应在web应用程序中使用
    DriverManager
    。而是使用数据源(带有连接池),但据我所知,当我使用JNDI时,我遇到了与上述相同的问题。我必须在webcontainer的配置中明确指定数据库。Hibernate允许您在代码中配置它,例如spring-jpa也是如此。非常感谢您的详细回答。您对这种方法的内存消耗有经验吗?目前大约有1000个客户,这意味着我必须缓存1000个EntityManagerFactory实例和1000个DB连接池。另一个问题:对于DB连接池,我可以使用JNDI,对吗?哇,1000个客户!对你的生意有好处:)但你是