首页 > 解决方案 > Jboss 7.2 txn 管理到 Spring 托管 PlatformTransactionManager - 锁定表

问题描述

从 JBoss 容器管理的 TransactionManagement 迁移到 Spring 管理的事务管理器 - (死)在插入表时锁定表并且不关闭 txn/session 并且从应用程序跟踪 txns 也会得到相同的错误。此行为仅在使用之后PlatformTransactionManager,并且在一个 jvm/appserver 以 0s 间隔在此表上选择而另一个 jvm/appserver 在同一个表上运行 insert 并且 insert txn 挂起的情况下。我必须杀死 txnshow full PROCESSLIST; kill <pid>;才能释放锁,以便下一个 sql 可以继续。

已锁定表的 MySQL 查询状态为“发送到客户端

发送给客户

服务器正在向客户端写入数据包。

任何帮助/提示表示赞赏。

Caused by: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
        at com.mysql@8.0.16//com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:123)
        at com.mysql@8.0.16//com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
        at com.mysql@8.0.16//com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
        at com.mysql@8.0.16//com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:955)
        at com.mysql@8.0.16//com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1094)
        at com.mysql@8.0.16//com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdateInternal(ClientPreparedStatement.java:1042)
        at com.mysql@8.0.16//com.mysql.cj.jdbc.ClientPreparedStatement.executeLargeUpdate(ClientPreparedStatement.java:1345)
        at com.mysql@8.0.16//com.mysql.cj.jdbc.ClientPreparedStatement.executeUpdate(ClientPreparedStatement.java:1027)
        at org.jboss.ironjacamar.jdbcadapters@1.4.11.Final-redhat-00001//org.jboss.jca.adapters.jdbc.WrappedPreparedStatement.executeUpdate(WrappedPreparedStatement.java:537)
        at org.hibernate@5.3.7.Final-redhat-00001//org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:175)

数据源:

<datasource jndi-name="java:jboss/datasources/my-ds" pool-name="my-ds" enabled="true" use-ccm="true" statistics-enabled="true">
                    <connection-url>jdbc:mysql://XXXXXXXXXXXX:3306/dbName?useSSL=false&amp;characterEncoding=UTF-8&amp;serverTimezone=US/Eastern</connection-url>
                    <driver>mysql</driver>
                    <pool>
                        <min-pool-size>1</min-pool-size>
                        <max-pool-size>10</max-pool-size>
                        <flush-strategy>FailingConnectionOnly</flush-strategy>
                    </pool>
                    <security>
                        <user-name>user</user-name>
                        <password>password</password>
                    </security>
                    <validation>
                        <valid-connection-checker class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker"/>
                        <check-valid-connection-sql>select 1 from dual</check-valid-connection-sql>
                        <validate-on-match>false</validate-on-match>
                        <background-validation>true</background-validation>
                        <background-validation-millis>30000</background-validation-millis>
                        <use-fast-fail>false</use-fast-fail>
                        <exception-sorter class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter"/>
                    </validation>
                    <timeout>
                        <set-tx-query-timeout>false</set-tx-query-timeout>
                        <idle-timeout-minutes>10</idle-timeout-minutes>
                        <query-timeout>0</query-timeout>
                        <allocation-retry>3</allocation-retry>
                        <allocation-retry-wait-millis>2500</allocation-retry-wait-millis>
                    </timeout>
                </datasource>
                <drivers>
                    <driver name="oracle" module="com.oracle">
                        <xa-datasource-class>oracle.jdbc.xa.client.OracleXADataSource</xa-datasource-class>
                    </driver>
                    <driver name="mysql" module="com.mysql">
                        <xa-datasource-class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
                    </driver>
                </drivers>

持久性.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
     http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd"
             version="2.2">

    <persistence-unit name="unitName" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <non-jta-data-source>java:jboss/datasources/my-ds</non-jta-data-source>
        <class>com.company.entity1</class>
        <class>com.company.entity2</class>
        <class>com.company.entity3</class>
...
        <properties>
            <property name="jboss.as.jpa.managed" value="false"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
            <property name="hibernate.show_sql" value="false"/>
            <property name="hibernate.generate_statistics" value="false"/>
            <property name="hibernate.id.new_generator_mapping" value="true"/>
            <property name="org.hibernate.envers.audit_table_suffix" value="_history"/>
            <property name="org.hibernate.envers.revision_field_name" value="history_info_id"/>
            <property name="org.hibernate.envers.revision_type_field_name" value="history_info_type"/>
        </properties>

    </persistence-unit>
    @Configuration
public class JpaConfig {

    @Bean
    public PersistenceUnitManager persistenceUnitManager() {
        DefaultPersistenceUnitManager persistenceUnitManager = new DefaultPersistenceUnitManager();
        persistenceUnitManager.setPersistenceXmlLocations("classpath*:META-INF/persistence.xml");
        persistenceUnitManager.setDataSourceLookup(new JndiDataSourceLookup());
        return persistenceUnitManager;
    }

    @Primary
    @Bean("someEntityManagerFactory")
    public EntityManagerFactory somentityManagerFactory(PersistenceUnitManager persistenceUnitManager) {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setPersistenceXmlLocation("classpath*:META-INF/persistence.xml");
        entityManagerFactoryBean.setPersistenceUnitManager(persistenceUnitManager);
        entityManagerFactoryBean.setPersistenceUnitName("unitName");
        entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        entityManagerFactoryBean.afterPropertiesSet();
        return entityManagerFactoryBean.getObject();
    }

    @Bean("someJpaTransactionManager")
    public PlatformTransactionManager someJpaTransactionManager(
            @Qualifier("someEntityManagerFactory") EntityManagerFactory entityManagerFactory){
        JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
        jpaTransactionManager.setEntityManagerFactory(entityManagerFactory);
        jpaTransactionManager.afterPropertiesSet();
        return jpaTransactionManager;
    }

交易状态:

public static TransactionStatus getTransactionStatus(PlatformTransactionManager platformTransactionManager){
        DefaultTransactionDefinition txDef = new DefaultTransactionDefinition(PROPAGATION_REQUIRES_NEW);
        txDef.setTimeout(transactionTimeout.get().intValue());
        return platformTransactionManager.getTransaction(txDef);
    }

//Usage: jpaTransactionManager.commit(txnStatus);

标签:spring-tx, PlatformTransactionManager, spring-5.1.5

标签: mysqlspringjbossjboss7.xspring-transactions

解决方案


这可能会有所帮助

注释类型启用事务管理

@Target(value=TYPE)
 @Retention(value=RUNTIME)
 @Documented
 @Import(value=TransactionManagementConfigurationSelector.class)

public @interface EnableTransactionManagement 启用 Spring 的注解驱动事务管理功能,类似于 Spring 的 XML 命名空间中的支持。用于@Configuration 类,如下所示:

 @Configuration
 @EnableTransactionManagement
 public class AppConfig {

     @Bean
     public FooRepository fooRepository() {
         // configure and return a class having @Transactional methods
         return new JdbcFooRepository(dataSource());
     }

     @Bean
     public DataSource dataSource() {
         // configure and return the necessary JDBC DataSource
     }

     @Bean
     public PlatformTransactionManager txManager() {
         return new DataSourceTransactionManager(dataSource());
     }
 }

作为参考,可以将上面的示例与以下 Spring XML 配置进行比较:

 <beans>

     <tx:annotation-driven/>

     <bean id="fooRepository" class="com.foo.JdbcFooRepository">
         <constructor-arg ref="dataSource"/>
     </bean>

     <bean id="dataSource" class="com.vendor.VendorDataSource"/>

     <bean id="transactionManager" class="org.sfwk...DataSourceTransactionManager">
         <constructor-arg ref="dataSource"/>
     </bean>

 </beans>

在上述两种情况下,@EnableTransactionManagement 和负责注册必要的 Spring 组件,这些组件为注释驱动的事务管理提供支持,例如 TransactionInterceptor 和基于代理或 AspectJ 的建议,当 JdbcFooRepository 的 @ 时将拦截器编织到调用堆栈中调用事务方法。两个示例之间的细微差别在于 PlatformTransactionManager bean 的命名:在 @Bean 的情况下,名称是“txManager”(根据方法的名称);在 XML 的情况下,名称是“transactionManager”。默认情况下查找名为“transactionManager”的bean是硬连线的,但是@EnableTransactionManagement更灵活;它将回退到容器中任何 PlatformTransactionManager bean 的按类型查找。

对于那些希望在 @EnableTransactionManagement 和要使用的确切事务管理器 bean 之间建立更直接关系的人,可以实现 TransactionManagementConfigurer 回调接口 - 请注意下面的 implements 子句和 @Override 注释方法:

@Configuration
 @EnableTransactionManagement
 public class AppConfig implements TransactionManagementConfigurer {

     @Bean
     public FooRepository fooRepository() {
         // configure and return a class having @Transactional methods
         return new JdbcFooRepository(dataSource());
     }

     @Bean
     public DataSource dataSource() {
         // configure and return the necessary JDBC DataSource
     }

     @Bean
     public PlatformTransactionManager txManager() {
         return new DataSourceTransactionManager(dataSource());
     }

     @Override
     public PlatformTransactionManager annotationDrivenTransactionManager() {
         return txManager();
     }
 }

这种方法可能是可取的,因为它更明确,或者为了区分同一容器中存在的两个 PlatformTransactionManager bean 可能是必要的。顾名思义,annotationDrivenTransactionManager() 将用于处理@Transactional 方法。有关详细信息,请参阅 TransactionManagementConfigurer Javadoc。mode() 属性控制如何应用建议:如果模式是 AdviceMode.PROXY(默认),那么其他属性控制代理的行为。请注意,代理模式只允许通过代理拦截呼叫;同一类中的本地调用不能以这种方式被拦截。

请注意,如果 mode() 设置为 AdviceMode.ASPECTJ,则 proxyTargetClass() 属性的值将被忽略。另请注意,在这种情况下,spring-aspects 模块 JAR 必须存在于类路径中,编译时编织或加载时编织将方面应用于受影响的类。这种情况下不涉及代理;本地电话也将被拦截。


推荐阅读