5

永続層にJPAを活用するSpringMVCアプリケーションを作成しようとしています。残念ながら、Springがそれを注入していないように見えるため、EntityManagerにアクセスするときにNullPointerExceptionが発生していました。私の構成はすべて@EnableWebMvcを使用したアノテーションベースです。検索した後、DAOに@Transactionalを追加し、@Configurationクラスに@EnableTransactionManagementを追加しました。次に、データソースがないというエラーが発生しました。おそらく、@EnableTransactionManagementを持つクラスはTransactionManagementConfigurerを実装する必要があります。ただし、データソースを作成する方法と、persistence.xmlからデータソースを取得できない理由を理解するのに問題があります。

EntityManagerをDAOに注入するための支援をいただければ幸いです。

私の@Configurationクラス

@Configuration
@EnableWebMvc
@EnableTransactionManagement
@ComponentScan("com.example.myapp")
public class MvcConfig extends WebMvcConfigurerAdapter 
        implements TransactionManagementConfigurer {

private static final boolean CACHE_ENABLED = true;
private static final String TEMPLATE_PATH = "/WEB-INF/freemarker";
private static final String TEMPLATE_SUFFIX = ".ftl";

private static final Logger LOG = Logger.getLogger( MvcConfig.class );

@Override
public void addResourceHandlers( ResourceHandlerRegistry registry ) {
    registry.addResourceHandler( "/stylesheets/**" ).addResourceLocations( "/stylesheets/" );
}

@Bean
public FreeMarkerConfigurer configureFreeMarker() {
    final FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
    configurer.setTemplateLoaderPath( TEMPLATE_PATH );
    return configurer;
}

@Bean
public ViewResolver configureViewResolver() {
    final FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
    resolver.setCache( CACHE_ENABLED );
    resolver.setSuffix( TEMPLATE_SUFFIX );
    return resolver;
}

@Bean
@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
    return new DataSourceTransactionManager();
}

}

私のDAO

@Component
@Transactional
public class MyDAO {

    private static final Logger LOG = Logger.getLogger( MyDAO.class );

    @PersistenceContext
    private EntityManager entityManager;

    public MyClass getMyClass() {
        LOG.debug( "getMyClass()" );
        final CriteriaQuery<MyClass> query = criteriaBuilder.createQuery( MyClass.class );
        // more code here, but it breaks by this point
        return myData;
    }

}

私の更新されたコード

ほぼすべてが機能するようになりました。EntityManagerが適切に挿入されています。ただし、トランザクションは機能していません。RESOURCE_LOCALアプローチを使用しようとするとエラーが発生するため、JTA管理のトランザクションを調べています。DAOメソッドのいずれかに@Transactionalを追加すると、トラブルシューティングに役立つログファイルにそれ以上の詳細がない「ロールバック用にマークされたトランザクション」エラーが発生します。基本的な読み取り専用選択から注釈を削除すると、選択は完全に正常に機能します(選択専用メソッドに注釈を付ける必要があるかどうかはわかりません)。ただし、db書き込みを実行するメソッドでこれを実行する必要があることは明らかです。コードをデバッグすると、データは完全に正常に取得されているようです。ただし、メソッドから戻ると、javax.transaction.RollbackExceptionがスローされます。

私の@Configurationクラス

@Configuration
@EnableWebMvc
@EnableTransactionManagement
@ComponentScan("com.example.myapp")
public class MvcConfig extends WebMvcConfigurerAdapter {

private static final boolean CACHE_ENABLED = true;
private static final String TEMPLATE_PATH = "/WEB-INF/freemarker";
private static final String TEMPLATE_SUFFIX = ".ftl";

private static final Logger LOG = Logger.getLogger( MvcConfig.class );

@Override
public void addResourceHandlers( ResourceHandlerRegistry registry ) {
    registry.addResourceHandler( "/stylesheets/**" ).addResourceLocations( "/stylesheets/" );
}

@Bean
public FreeMarkerConfigurer configureFreeMarker() {
    final FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
    configurer.setTemplateLoaderPath( TEMPLATE_PATH );
    return configurer;
}

@Bean
public ViewResolver configureViewResolver() {
    final FreeMarkerViewResolver resolver = new FreeMarkerViewResolver();
    resolver.setCache( CACHE_ENABLED );
    resolver.setSuffix( TEMPLATE_SUFFIX );
    return resolver;
}

@Bean
public PlatformTransactionManager transactionManager() {
    return new JtaTransactionManager();
}

@Bean
public AbstractEntityManagerFactoryBean entityManagerFactoryBean() {
    LocalEntityManagerFactoryBean factory = new LocalEntityManagerFactoryBean();
    factory.setPersistenceUnitName( "my_db" );
    return factory;
}

}
4

2 に答える 2

9

私のアプリケーションでは、TransactionManagerConfigurerインターフェイスを実装していません。次のコードを使用してJPAを構成します(Hibernate実装を使用)。構成クラスでも同じことができます。

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
        LocalContainerEntityManagerFactoryBean factoryBean = 
                new LocalContainerEntityManagerFactoryBean();

        factoryBean.setDataSource(dataSource());
        factoryBean.setPackagesToScan(new String[] {"com.dimasco.springjpa.domain"});

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setShowSql(true);
        //vendorAdapter.setGenerateDdl(generateDdl)

        factoryBean.setJpaVendorAdapter(vendorAdapter);

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

        factoryBean.setJpaProperties(additionalProperties);


        return factoryBean;
    }

    @Bean
    public DataSource dataSource() {
        final ComboPooledDataSource dataSource = new ComboPooledDataSource();

        try {
            dataSource.setDriverClass(driverClass);
        } catch (PropertyVetoException e) {
            throw new RuntimeException(e);
        }

        dataSource.setJdbcUrl(jdbcUrl);
        dataSource.setUser(user);
        dataSource.setPassword(password);
        dataSource.setMinPoolSize(3);
        dataSource.setMaxPoolSize(15);
        dataSource.setDebugUnreturnedConnectionStackTraces(true);

        return dataSource;
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactoryBean().getObject());

        return transactionManager;
    }

    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
        return new PersistenceExceptionTranslationPostProcessor();
    }

これがお役に立てば幸いです)

編集:

JNDIルックアップを使用してデータソースを取得できます。

@Bean
public DataSource dataSource() throws Exception {
   Context ctx = new InitialContext();
   return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}

詳細については、この記事をご覧ください。JndiDatasourceConfigクラスの例があります。

編集2: プロジェクトにpersistence.xmlがありますが、空です:

<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="JPA_And_Spring_Test">
    </persistence-unit>
</persistence>

また、Java構成で永続的なユニット名を指定しませんでした。

于 2013-02-10T18:31:21.553 に答える
1

XMLベースの構成を使用している場合でも、以下が役立つ場合があります。

https://github.com/springinpractice/sip13/blob/master/helpdesk/src/main/resources/spring/beans-repo.xml

Spring Data JPAを使用しますが、それを行う必要はありません。使用する

 @PersistenceContext private EntityManager entityManager;

(ただし、Spring Data JPAは、すぐに使用できる非常に有能なDAOを提供するため、検討してください。)

補足:DAOの場合は、よりも優先@Repositoryして@Componentください。どちらもコンポーネントスキャンで機能しますが、@Repository使用目的をより適切に説明します。

于 2013-02-12T18:37:02.223 に答える