JPA Hibernate动态实体映射&运行时持久性

时间:2017-11-16 13:22:39

标签: hibernate jpa spring-data spring-data-jpa hibernate-mapping

基本上我们有一个spring启动应用程序,它要求用户可以定义他/她自己的字段集,并且这些字段应该在运行时通过JPA / Hibernate保存在他们自己的类/表中。这些类将通过bytebuddy动态生成。

所有这些都应该动态完成而无需重新启动应用程序。 Hibernate动态映射不是一个选项,因为我们将完全创建新类并重新映射它们。

我还考虑了EAV模型,但由于我们需要为每组数据使用单独的表,因此无法使用,因此JSON不能在同一个表中混合使用。

我考虑的第一个解决方案是代理EntityManagetFactory,当我们有一个新的实体进行映射时,我将重新创建EntityManagetFactory并将新的映射实体添加到其中,I&#39 ; ll还将hbm2ddl.auto设置为"更新"为了确保创建新的表格方案。

问题是我不知道其他可能需要代理的类,我相信我必须代理Hibernate SessionFactory,但我不确定还需要多少其他类。被重新创建和代理,我相信这是一条复杂的道路。

另一种解决方案是使用Hibernate OGM在SQL和NoSQL解决方案之间进行混合,但在这种情况下,我将松散与现有SQL实体之间的任何关系,而且我不赞成运行第二个NoSQL DB。

我还可以探索其他任何解决方案吗?

编辑:

我会使用bytebuddy来动态生成新类,并且它们会有@Entity注释,生成的类被写入临时jar文件(例如/tmp/myjar.jar)

使用BeanPostProcessor.postProcessAfterInitialization我将使用代理类替换LocalContainerEntityManagerFactoryBean。

我还使用LocalContainerEntityManagerFactoryBean .setPersistenceUnitPostProcessors添加一个额外的处理器来处理新创建的jar中的类

所以现在用bytebuddy创建新类之后,我会手动调用LocalContainerEntityManagerFactoryBeanProxy.afterProperties来完成引导JPA和hibernate层的所有工作,我也设置了" hibernate.hbm2ddl.auto"财产到"更新"这样就可以创建模式(我知道在生产环境中这样做很危险)

2 个答案:

答案 0 :(得分:0)

Hibernate将实体映射到表,并在引导期间构建元数据。因此,在应用程序运行时,您无法动态修改它。

但是,只要您在不修改现有结构的情况下继续添加新表,就可以在架构级别解决此问题:

  1. 您可以根据需要进行课程更改。
  2. 您构建项目工件。
  3. 您将新项目工件部署到新服务器。
  4. 您可以从负载均衡器将流量从旧服务器实例切换到新服务器实例,而不会出现任何停机。
  5. 或者,只使用像MongoDB这样的NoSQL数据库和Hibernate OGM,因为无论如何你的要求都不适合关系数据库。

    但是,如果您已经使用了RDBMS,那么it's simpler to just use JSON而不是仅仅因为这个原因而切换到NoSQL数据库。

答案 1 :(得分:0)

首先,问题是如何在热部署中添加实体类。我们可以通过一些交换工具(spring-boot-devtools或maven copy resource)来实现。 其次,要构建不同的实体模型,可以使用JPA继承(https://en.wikibooks.org/wiki/Java_Persistence/Inheritance) 或者jpa rowmapper。

但是,我认为将Json对象持久化为数据库中的文本更为轻松,并使服务的使用者(前端或其他服务)解析它。

另一种方法是尝试通过类路径在运行时加载类。我将尝试持久化Json对象作为文本及其在ddbb中的类型。然后在application.properties中创建其类型和类路径的映射(数据的类java),然后执行以下操作:

static{
     Map<String, String> typeClassPathMap = ......// from properties
     File file = new File("c:\\class-path\\");
    // Convert File to a URL
    URL url = file.toURL();          
    // file:/c:/myclasses/
    URL[] urls = new URL[]{url};
    ClassLoader cl = new URLClassLoader(urls);
}

 Class<?> loadClass(String type){

    Class clazz = urlClassLoader.loadClass(typeClassPathMap.get(type));
 }

它将在运行时读取类,并可通过属性进行配置。之后,对于每种类型的数据,我们在属性文件中定义su类型和类路径。当数据来自ddbb时,我们使用它的类型来获取java类,并将其解析为object。当我们需要创建新类型的数据时,我们将类留在类路径中,并在属性中进行配置。