Spring Hibernate:重新加载实体映射

5
在 web 应用中,我们使用 Spring 3.2 和 Hibernate 4.1.1 实现插件式架构。插件可以在运行时添加和删除。我们为每个模块定义了一个单独的类加载器,并在 Spring 上创建了单独的子应用程序上下文。所有配置都使用注解完成,不再需要 XML 配置 bean。以下是 Spring Hibernate 配置类:
@Configuration
@EnableTransactionManagement
public class HibernateConfigurationFactory {

@Bean
public JndiObjectFactoryBean dataSource() {
    JndiObjectFactoryBean ds = new JndiObjectFactoryBean();
    ds.setJndiName("java:jboss/datasources/OurOwnDS");
    ds.setResourceRef(true);
    return ds;
}

@Bean
public LocalSessionFactoryBean sessionFactory() {
    LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
    sessionFactory.setPackagesToScan("com.foo.bar");
    sessionFactory.setDataSource((DataSource) dataSource().getObject());

    Properties hibernateProperties = new Properties();
    hibernateProperties.put("hibernate.hbm2ddl.auto", "update");

    sessionFactory.setHibernateProperties(hibernateProperties);

    return sessionFactory;
}

@Bean
public HibernateTransactionManager transactionManager() {
    HibernateTransactionManager transactionManager = new HibernateTransactionManager();
    transactionManager.setSessionFactory(sessionFactory().getObject());
    return transactionManager;
}

}

现在的问题是: 一些插件包含其自己的实体(+DAO)类,这些类在运行时与模块一起添加。
是否可以在Hibernate上创建某种分离的上下文(就像我们在Spring上做的那样),甚至添加/重新加载其他实体类?
重新加载EntityManager是否符合我的需求? 但是,在上下文中已经加载的实体会发生什么?
非常感谢任何提前给予的帮助和评论。
更新: 实际上,我做了以下事情并解决了该问题(但后来遇到了另一个问题)。 为每个模块/上下文创建一个新的DataSource + SessionFactory + TransactionManager,并将它们插入到新的子ApplicationContext中。 现在,我使用类加载器来扫描所有注释类,并通过使用手动注册它们到应用程序上下文和会话工厂来解决问题。
LocalSessionFactorybean#setAnnotatedClasses(...)

这个方法非常有效...但是...

接下来的问题: 我遇到了ClassNotFoundException,这似乎是一个类加载器的问题。 Hibernate使用系统类加载器而不是我的自定义插件类加载器。

有没有人知道如何将自己的类加载器注入到Hibernate中呢?

2个回答

2

Environment注入并将其用作Hibernate资源的额外来源是否可行?

@Configuration
@EnableTransactionManagement
public class HibernateConfigurationFactory {

    @Autowired
    Environment env;

    @Bean 
    public LocalSessionFactoryBean sessionFactory() {
        LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
        sessionFactory.setPackagesToScan("com.foo.bar");
        if (env.containsProperty("some.extra.classes.property") {
            sessionFactory.setAnnotatedClasses(some extrapolation here);
            // Or similarly add extra packages for scanning
            ...
        }
    }
}

关于额外的DAO bean,您可以使用@Profile,或者如果使用Spring 4,则使用@Conditional
编辑: Environment不是您创建的对象,而是“为您准备好的”对象。它基本上是属性源和配置文件集的容器。
良好的参考点是Spring参考文档的IoC容器章节。此外,您可以查看SpringSource博客。Chris Beams的几篇与Spring 3.1相关的文章很不错,但大部分内容都在那里。
例如,您可以使用以下内容启动子应用程序上下文:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
ConfigurableEnvironment environment = context.getEnvironment();

// this is how you set different properties per a sub context. 
Map subContextMap = new HashMap();
subContextMap.put("some.extra.classes.property", [unique value here]);
environment.getPropertySources().addFirst(new MapPropertySource("SUB_CTX_MAP", subContextMap);

// this is the generic configuration class(es).
context.register(HibernateConfigurationFactory.class);

context.refresh();

谢谢回复,看起来上面发布的代码是在应用程序启动时使用的(我还不知道类)。我需要一些东西,在应用程序已经启动后可以手动触发/添加包。但我会查看如何包含这个。 - NeoP5
是否可以创建一个新的/重新加载环境? - NeoP5
如果您使用单独的上下文(每个模块一个),那么每个上下文都有自己的环境对象,其中附加了不同的“PropertySource”,如果需要,可以为同一属性设置不同的值。 - Ori Dar
啊,不错 :) 这是一个好提示。目前我为每个模块创建的上下文都是全局应用程序上下文的子上下文。在创建上下文时,我将尝试定义一个新的环境对象。你有一些关于如何实现这个的示例代码吗? - NeoP5
@NeoP5,我会编辑我的回复来回答你的问题。 - Ori Dar

1

如果有人需要类似的解决方案,这里更新一下。

我通过以下方式解决了这个问题:

  • 使用当前根上下文作为父级创建一个新的ApplicationContext
  • 引入新的类加载器到该上下文以及自有Resource-Loader
  • 通过编程方式创建一个新的session-factory和transaction-manager到新的上下文中

    //创建数据源bean
    BeanDefinitionBuilder dataSourceBeanBuilder = BeanDefinitionBuilder.rootBeanDefinition(DataSourceConfiguration.class, "createDataSource");
    dataSourceBeanBuilder.addConstructorArgValue(descriptor.getDataSourceDescriptor().getJNDILookupName());
    dataSourceBeanBuilder.addConstructorArgValue(descriptor.getDataSourceDescriptor().isResourceRef());
    moduleContext.registerBeanDefinition("dataSource", dataSourceBeanBuilder.getBeanDefinition());
    
    //构建sessionFactory
    BeanDefinitionBuilder sessionFactoryBeanBuilder = BeanDefinitionBuilder.rootBeanDefinition(SessionFactoryFactory.class, "createSessionFactory");
    sessionFactoryBeanBuilder.addConstructorArgReference("dataSource");
    sessionFactoryBeanBuilder.addConstructorArgValue(module.getKey());
    sessionFactoryBeanBuilder.addConstructorArgValue(moduleContext.getModuleResourceLoader());
    sessionFactoryBeanBuilder.addConstructorArgValue(annotatedClasses);
    moduleContext.registerBeanDefinition("sessionFactory", sessionFactoryBeanBuilder.getBeanDefinition());
    
    //构建transactionManager
    BeanDefinitionBuilder transactionManagerBeanBuilder = BeanDefinitionBuilder.rootBeanDefinition(HibernateConfigurationFactory.class, "createTransactionManager");
    transactionManagerBeanBuilder.addConstructorArgReference("sessionFactory");
    moduleContext.registerBeanDefinition("transactionManager", transactionManagerBeanBuilder.getBeanDefinition());
  • 调用context.refresh()加载和初始化所有实体
  • 调用context.start()启动新的上下文

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接