升级 Jboss 服务器(WildFly 18.x)内的 Hibernate 从 3 版本升级到 4 版本。

huangapple go评论62阅读模式
英文:

Upgrade Hibernate from 3 to 4 inside Jboss server (WildFly 18.x)

问题

我试图将 Hibernate 从 3 升级到 4,但遇到了多个问题。以下是我们在 v3 中的配置。

@Bean
public LocalSessionFactoryBean sessionFactory(DataSource datasource) {
  LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
  sessionFactory.setDataSource(datasource);
  sessionFactory.setPackagesToScan("com.company.hs.service");
  sessionFactory.setHibernateProperties(hibernateProperties());
  return sessionFactory;
}

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

private Properties hibernateProperties() {
  Properties properties = new Properties();
  properties.put("hibernate.dialect", "com.company.hs.service.hibernate.MySQL5InnoDBIndexDialect");
  properties.put("hibernate.show_sql", Boolean.TRUE.toString());
  properties.put("hibernate.generate_statistics", Boolean.FALSE.toString());
  properties.put("transaction.factory_class", "org.hibernate.transaction.JTATransactionFactory");
  properties.put("transaction.manager_lookup_class", "org.hibernate.transaction.JBossTransactionManagerLookup");
  properties.put("hibernate.cache.use_query_cache", Boolean.TRUE.toString());
  properties.put("hibernate.cache.use_second_level_cache", Boolean.TRUE.toString());
  return properties;
}

在升级了依赖版本和类包之后,我成功编译并启动了应用程序。但是,在尝试执行任何写入数据库的操作后,我遇到了以下错误:

org.springframework.dao.InvalidDataAccessApiUsageException: 在只读模式下不允许写入操作(FlushMode.MANUAL):将您的 Session 转换为 FlushMode.COMMIT/AUTO,或从事务定义中删除 'readOnly' 标记。

经过大量的研究,似乎有多种选项可以解决这个问题。

选项1
https://stackoverflow.com/questions/25620303/how-can-i-globally-set-flushmode-for-hibernate-4-3-5-final-with-spring-4-0-6 中建议的那样,覆盖 OpenSessionInViewFilter。

尽管这似乎有所帮助,但在某些情况下会出现应用程序行为发生更改的情况(例如,当使用 @GeneratedValue(strategy = GenerationType.IDENTITY) 时,HibernateTemplate 的 persist 方法不会在原地更新实体 ID,因此我们必须使用 save 方法)。并且总体上,由于似乎事务管理在这里没有得到正确的启用,所以还要担心其他副作用。

选项2
正如 https://crunchtech.io/blog/migrating-from-hibernate-3-to-4-with/ 中建议的那样,我们可以从 JTATransactionFactory 切换到 CMTTransactionFactory。这似乎是我们想要继续的东西,因为我们希望 Spring 容器来管理事务。

在尝试执行 SQL 查询时,出现了以下错误:org.hibernate.TransactionException: 无法为容器事务注册同步。

供参考,只有这一部分从原始配置中更改:

properties.put("hibernate.transaction.factory_class", "org.hibernate.transaction.CMTTransactionFactory");
properties.put("hibernate.transaction.manager_lookup_class", "org.hibernate.transaction.JBossTransactionManagerLookup");
properties.put("hibernate.transaction.jta.platform", "org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform");

与之相反,Spring 的错误跟踪器建议采用完全相反的方法 - https://github.com/spring-projects/spring-framework/issues/15230

选项3 是使用围绕 @Transactional 执行刷新数据到数据库的 AOP 方案。

选项4 是使用 JPA。

我认为选项3和4的可能性很低。

互联网上的多个示例表明,从 Hibernate 3 迁移到 4 对于 Tomcat 应该可以很顺利,大部分问题出现在 Jboss/GlassFish 服务器上。不幸的是,我们的应用程序在 WildFly 中运行。

此时,我会对此表示感谢。首先,从一般使用范例开始,也许这里提到的选项完全不适用,我们需要使用不同的机制。或者我们遗漏了一些关键的配置。对应的依赖版本如下:

Spring - 4.0.5.RELEASE
Hibernate - 4.2.12.Final
WildFly - 18.0.1
英文:

I'm trying to upgrade hibernate from 3 to 4 and running into multiple issues. Here is a configuration we had for v3.

  @Bean
  public LocalSessionFactoryBean sessionFactory(DataSource datasource) {
    LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
    sessionFactory.setDataSource(datasource);
    sessionFactory.setPackagesToScan("com.company.hs.service");
    sessionFactory.setHibernateProperties(hibernateProperties());
    return sessionFactory;
  }

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


  private Properties hibernateProperties() {
    Properties properties = new Properties();
    properties.put("hibernate.dialect", "com.company.hs.service.hibernate.MySQL5InnoDBIndexDialect");
    properties.put("hibernate.show_sql", Boolean.TRUE.toString());
    properties.put("hibernate.generate_statistics", Boolean.FALSE.toString());

    properties.put("transaction.factory_class", "org.hibernate.transaction.JTATransactionFactory");
    properties.put("transaction.manager_lookup_class", "org.hibernate.transaction.JBossTransactionManagerLookup");

    properties.put("hibernate.cache.use_query_cache", Boolean.TRUE.toString());
    properties.put("hibernate.cache.use_second_level_cache", Boolean.TRUE.toString());

    return properties;
   }

After upgrading dependency version and class packages itself, I was able to compile and start application.

But then, after trying to execute any write operation against DB I'm getting following error:

org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.

After researching ton of information, there seems to be multiple options to go about it.

Option 1:
Override OpenSessionInViewFilter like suggested in https://stackoverflow.com/questions/25620303/how-can-i-globally-set-flushmode-for-hibernate-4-3-5-final-with-spring-4-0-6

Though it seems to help, there are multiple edge cases when app behavior changed (i.e. HIbernateTemplate's method persist does not update entity id in place while using @GeneratedValue(strategy = GenerationType.IDENTITY), so instead we have to use save method). And in general, concerned about other side effects since it seems that transaction management is not properly engaged here.

Option 2:
As it was suggested in https://crunchtech.io/blog/migrating-from-hibernate-3-to-4-with/ instead of using JTATransactionFactory we can switch to CMTTransactionFactory. Which is seems like something we want to proceed since we want Spring container to manage transactions.
Corresponding spring javadocs - https://docs.spring.io/spring-framework/docs/3.2.0.M1_to_3.2.0.M2/changes/docdiffs_org.springframework.orm.hibernate4.html

While trying to execute SQL query it fails with
org.hibernate.TransactionException: Could not register synchronization for container transaction.

For reference, only this piece changed from original configuration:

    properties.put("hibernate.transaction.factory_class", "org.hibernate.transaction.CMTTransactionFactory");
    properties.put("hibernate.transaction.manager_lookup_class", "org.hibernate.transaction.JBossTransactionManagerLookup");
    properties.put("hibernate.transaction.jta.platform", "org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform");

Controversially, Spring's bug tracker suggests completelly opposite approach - https://github.com/spring-projects/spring-framework/issues/15230

Option 3 would be using AOP aspect that will be executed around @Transactional for flushing data into DB.

Option 4 Use JPA

IMHO likellyhood of 3 and 4 is very low.

Multiple examples in internet suggest that migration of Hibernate from 3 -> 4 should work as a charm for Tomcat and most of the issues come when running in Jboss/GlassFish server.
Unfortunately, we run our application inside WildFly.

At this point, I'd appreciate any input on this. Starting with the question what is a general usage paradigm, maybe options mentioned here are completely off and we need to use different mechanism. Or we are missing some crucial piece of configuration.

Corresponding dependency versions

Spring - 4.0.5.RELEASE
Hibernate - 4.2.12.Final
WildFly - 18.0.1

答案1

得分: 0

我确实设法使其与Spring 4和Hibernate 5一起在WildFly内运行,无需自定义OpenSessionInViewFilter或指定容器特定属性(比如hibernate.transaction.factory_classhibernate.transaction.manager_lookup_class)。

成功的关键是正确使用@Transactional注解,并稍微调整查询本身。

更甚者,在我的测试应用中启用JTA事务属性(如此处所述)会导致诸如运行时异常上不正确的回滚等副作用。这些是我用来启用它的属性:

properties.put("hibernate.transaction.jta.platform", "JBossAS");
properties.put("hibernate.transaction.coordinator_class", "jta");

没有指定这些属性的相同代码会如预期地回滚所有中间的数据库条目。尚不清楚为什么会这样,但我们首先没有充分的理由使用JTA事务。

英文:

I did manage to get it working with Spring 4 Hibernate 5 running within WildFly w/o customizing OpenSessionInViewFilter or specifying container-specific properies (like hibernate.transaction.factory_class or hibernate.transaction.manager_lookup_class).
Key to success was proper usage of @Transactional annotation and little bit of tweeking queries itself.

Even more to that, in my test app enabling JTA transactions properties (like prescribed here) caused side effects like incorrect rollback on runtime exceptions.
These are the properties I was using to enable it:

properties.put("hibernate.transaction.jta.platform", "JBossAS");
properties.put("hibernate.transaction.coordinator_class", "jta");

Same code w/o these being specified rollbacks all intermediate DB entries as expected. Didn't known why is that yet, but there is no good reason for us to use JTA transactions in the first place.

huangapple
  • 本文由 发表于 2020年9月29日 01:46:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/64107109.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定