13

編集:この問題に興味がある人は誰でも、問題の分析と関連する解決策を質問の最後に提供します。

Spring 3.2、Hibernate 4.1、Spring Data JPA 1.3、および Apache CXF 2.5 (特に JAX-RS モジュール) を使用している Web アプリケーションのモジュールを構成しています。私は次の構成を持っています(これは完全に正常に機能していますが、簡潔にするために詳細は省略されています):

  @Bean(name = "entityManagerFactory")
  public LocalContainerEntityManagerFactoryBean getEntityManagerFactory() throws SQLException{
    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    //...    
    return factory;
  }

  @Bean(name = "transactionManager")
  public JpaTransactionManager getTransactionManager() throws SQLException{
    JpaTransactionManager manager = new JpaTransactionManager();
    //...    
    return manager;
  }

  @Bean(name = "persistenceExceptionTranslator")
  public PersistenceExceptionTranslator getPersistenceExceptionTranslator(){
    return new HibernateExceptionTranslator();
  }

私の問題は、独自の を定義するいくつかの外部モジュールに依存する必要があることですPlatformTransactionManager。そのため、同時により多くのトランザクション マネージャーを使用していることに気付きます。この問題はTransactional.html#value()によって簡単に対処できるため、使用する必要がある場所では常に、@Transactionalそのトランザクションに使用する必要があるトランザクション マネージャーの名前でアノテーションを修飾しました。
外部モジュールの標準を満たすために、モジュールで定義するトランザクション マネージャーの名前をより意味のある名前に変更したいと考えています。したがって、たとえば、そのマネージャーを次のようにexternalModule1定義します。externalModule1TransactionManager

  @Bean(name = "myModuleransactionManager")
  public JpaTransactionManager getTransactionManager() throws SQLException{
    JpaTransactionManager manager = new JpaTransactionManager();
    //...    
    return manager;
  }

残念ながら、この変更を行うと、これは非常に簡単に思えます (それに@Transactional#value()応じて の使用法を変更すると、例外が発生します。

java.lang.RuntimeException: org.apache.cxf.interceptor.Fault: No bean named 'transactionManager' is defined
    at org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver.onMessage(AbstractFaultChainInitiatorObserver.java:110)
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:323)
    at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:123)
    at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:207)
    at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:213)
    at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:154)
    at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:126)
    at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:185)
    at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doGet(AbstractHTTPServlet.java:113)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
    at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:164)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
    at java.lang.Thread.run(Thread.java:662)
Caused by: org.apache.cxf.interceptor.Fault: No bean named 'transactionManager' is defined
    at org.apache.cxf.service.invoker.AbstractInvoker.createFault(AbstractInvoker.java:155)
    at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:121)
    at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:167)
    at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:94)
    at org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:58)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
    at java.util.concurrent.FutureTask.run(FutureTask.java:138)
    at org.apache.cxf.workqueue.SynchronousExecutor.execute(SynchronousExecutor.java:37)
    at org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:106)
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263)
    ... 25 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'transactionManager' is defined
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:568)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1099)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:278)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:198)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.determineTransactionManager(TransactionAspectSupport.java:246)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:100)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.data.jpa.repository.support.LockModeRepositoryPostProcessor$LockModePopulatingMethodIntercceptor.invoke(LockModeRepositoryPostProcessor.java:92)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
    at sun.proxy.$Proxy98.save(Unknown Source)
    at myModule.package.SomeOtherClass.someOtherMethod(SomeOtherClass.java:114)
    at myModule.package.SomeOtherClass$$FastClassByCGLIB$$2bda5a73.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:698)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)
    at myModule.package.SomeClass$$EnhancerByCGLIB$$37044080.myMethod(<generated>)
    at myModule.package.SomeClass.someMethod(SomeClass.java:64)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:173)
    at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:89)
    ... 34 more

特に注目したいのは、

myModule.package.SomeOtherClass.someOtherMethod(SomeClass.java:114)

myModule.package.SomeClass.someMethod(SomeClass.java:64)

彼らのコードは次のようになります

@Transactional("myModuleransactionManager")
public ... someOtherMethod(){
   ...
}

public ... someMethod(){
   ...
}

したがって、私の理解では、この構成は機能するはずですが、なぜその例外がスローされるのですか? 標準の名前付きトランザクション マネージャーは必要ですか? それとも cxf のせいでしょうか?同じアプリケーション ( example 1example2 )内の複数のトランザクション マネージャーに関連するいくつかの質問を見つけましたが、それらの質問で受け入れられた回答が私の解決策につながります。私は何を誤解し、間違っていますか?
この長い質問をここまで読んでくれてありがとう!

Michail の回答に基づいて完全な説明を提供するために編集します。Spring Data JPAを使用すると、データベースに接続するためのリポジトリ インターフェイスを定義する必要があります。someOtherMethod実際、次のように定義されている私のリポジトリの1つを呼び出しています:

@Repository("myRepository")
@Transactional(propagation = Propagation.NESTED, value = "myModuleransactionManager")
public interface MyRepository extends JpaRepository<MyEntity, Integer>
{

}

これも問題ないように見えますが、JpaRepository実装のソース コードを見ると (したがって、org.springframework.data.jpa.repository.support.SimpleJpaRepository(save他の更新メソッドと同様に) に@Transactional. という注釈が付けられていることがわかりました。SimpleJpaRepository

    @Transactional
    public <S extends T> S save(S entity) {

        if (entityInformation.isNew(entity)) {
            em.persist(entity);
            return entity;
        } else {
            return em.merge(entity);
        }
    }

したがって、Spring Data JPA を使用する場合、デフォルトのトランザクション マネージャー ( という名前のものtransactionManager) は必須です。私の目標には悪いですが、少なくともこれで終わりだとわかりました!

4

6 に答える 6

10

someOtherMethod他のコンポーネント(メソッド@Transactionalを持つクラス)を呼び出すように見えます。そして、 (空の)アノテーション(という名前のデフォルトのBeanを使用)saveがあると思います。@Transactinal()transactionManager

スタックトレースに2つTransactionInterceptorの位置が表示される場合があります。詳細を教えてください。

于 2013-02-28T21:53:20.530 に答える
0

あなたの質問は概念的に非常に興味深いものでした。そして、こうして私の長い間忘れられていた概念のいくつかを修正することができました。Java構成側の制限のようです。したがって、間に少しのxmlを使用してから、transactionmanagerのようなものを指定する必要があります。

<tx:annotation-driven transaction-manager="myModuletransactionManager"/>

次に、transactionManagerを使用できます。デフォルトのSimpleJpaRepository も、新しいもののみを使用します。

更新:または、Configを介してこれを使用することもできますが、今ではEnableTransactionManagementのようです

于 2013-03-01T12:38:01.300 に答える
0

サービス層で @Transactional を修飾しました。そして、次のようにSpring Dataリポジトリでトランザクション管理を無効にできるようです:

<jpa:repositories base-package="ru.xdsoft.conn.thanksapp.thanks.dao.repository"
                  entity-manager-factory-ref="thanksEntityManagerFactory"
                  enable-default-transactions="false"
/>
<jpa:repositories base-package="ru.xdsoft.conn.thanksapp.orgstruct.dao"
                  entity-manager-factory-ref="orgStructEntityManagerFactory"
                  enable-default-transactions="false"
/>

100%確実ではありませんが、エラーはなくなりました。ここで見つけました:https://github.com/spring-projects/spring-data-jpa/blob/master/src/test/resources/org/springframework/data/jpa/repository/support/disable-default-transactions。 xml

于 2016-09-22T19:09:48.167 に答える
0

私はそれを数回行ったので、Java コード (xml なし) で完全に行う方法を次に示します。ただし、私はロンボクを使用していますが、これを強くお勧めします。私は質問された問題のみに焦点を当てているので、これまでにこれを行ったことがない場合は、Spring ドキュメントを読んで、JPA ダイアレクトとスプリング データソース ドライバー クラスに関する追加の詳細を構成してください。

findAll()説明:またはsave()TransactionInterceptor のような JPA 埋め込みメソッドを呼び出すと、デフォルトが検索されます。これは、Hibernate および JPA を介して複数のデータベースに接続するため"transactionManager" に必要なものです。

  1. で変数を定義しますapplication.properties
first.datasource.url=my/database/url/example
first.datasource.username=username-example
first.datasource.password=password-example

second.datasource.url=my/database/url/example
second.datasource.username=username-example
second.datasource.password=password-example
  1. databaseConfig を作成します
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
        entityManagerFactoryRef = "firstEntityManagerFactory", basePackages = {
        "be.company.appname.repository.firstdatabase", "be.company.appname.config.firstdatabase"
})
public class FirstDatabaseConfig {

    @Value("${first.datasource.url}")
    private String url;
    @Value("${first.datasource.username}")
    private String username;
    @Value("${first.datasource.password}")
    private String password;
    @Value("${spring.datasource.driver-class-name}")
    private String driver;

    @Primary
    @Bean(name = "firstDataSourceProperties")
    @ConfigurationProperties("first.datasource")
    public DataSourceProperties dataSourceProperties() {
        return new DataSourceProperties();
    }

    @Primary
    @Bean(name = "firstDataSource")
    @ConfigurationProperties("first.datasource.configuration")
    public DataSource dataSource(@Qualifier("firstDataSourceProperties") DataSourceProperties dataSourceProperties) {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setRemoveAbandonedOnBorrow(true);
        dataSource.setRemoveAbandonedOnMaintenance(true);
        dataSource.setInitialSize(10);
        dataSource.setMaxTotal(20);
        dataSource.setValidationQuery("select 1 from MY_SCHEMA.TABLE");
        dataSource.setValidationQueryTimeout(900_000);
        dataSource.setTestWhileIdle(true);
        dataSource.setLogAbandoned(true);
        dataSource.setTestOnReturn(true);
        dataSource.setTestOnBorrow(true);
        dataSource.setDefaultAutoCommit(false);
        return dataSource;
    }

    @Primary
    @Bean(name = "firstEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(
            EntityManagerFactoryBuilder builder,
            @Qualifier("firstDataSource") DataSource firstDataSource
    ) {
        return builder
                .dataSource(firstDataSource)
                .packages("be.company.appname.model.firstdatabase")
                .persistenceUnit("first")
                .build();
    }

    @Primary
    @Bean(name = "firstTransactionManager")
    public PlatformTransactionManager transactionManager(
            @Qualifier("firstEntityManagerFactory") EntityManagerFactory firstEntityManagerFactory
    ) {
        return new JpaTransactionManager(firstEntityManagerFactory);
    }
}

これに関するいくつかの注意事項:

  • に注意して@Primaryください!これは、databaseConfigs の 1 つでのみ必要です。2 番目のデータベースでは、同じコードを使用して、名前の変更 (例: firstEntityManagerFactory が secondEntityManagerFactory になるなど)、適切な変数の変更、ValidationQuery の変更、正しいパッケージの定義など、明らかな変更を加えることができます。
  • 2 番目の databaseConfig では、宣言"be.company.appname.config.firstdatabase"から削除しました。basePackages = {}リポジトリ パッケージへのポインタだけで十分です。
  1. firstEntityManagerFactoryBean内で定義したパッケージに databaseObject を作成します。
@Entity
@Table(name = "USER")
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MyUser {

    @Id
    @Column(name = "ID")
    private Long id;

    @Column(name = "USERNAME")
    private String userName;

    @Column(name = "UID")
    private String uid;

    @Column(name = "FIRST_NAME")
    private String firstName;

    @Column(name = "LAST_NAME")
    private String lastName;
  • @Tableデータベーステーブルの正確な名前です
  • @Columndatabasetable-column の正確な名前です。独自のフィールド変数が一致する必要はありませんが、私は習慣からそうしています (たとえば、宣言@Column(name = "USERNAME") private String name; も機能します)。
  1. databaseConfig クラスで宣言したパッケージにリポジトリを作成します
@Repository
@Transactional(value = "firstTransactionManager")
public interface MyUserRepository extends JpaRepository<MyUser, Long> {

    List<MyUser> findAll();
}

質問者の例外が発生する原因は何ですか? 例えば:

MyUserRepository.findById(1L)リポジトリでメソッドを宣言せずに呼び出します。これは、JPA default embedded の省略形として知られています。独自のJPA 短縮クエリの詳細を確認してください。transactionManagerリポジトリで宣言されていない場合、アプリケーションはリポジトリ インターフェイスをバイパスしてデフォルトを探します。ただし、メソッドを宣言することにより、アプリケーションは独自のカスタムを探すことを認識しますfirstTransactionManager

注:の作成は、BasicDataSource使用しているデータベースによって異なる場合があります。DB2DialectAS400への接続に使用しています。

于 2021-06-04T13:59:20.930 に答える