如何以编程方式配置TransactionManager

13

我这里有一个基于Spring MVC的应用程序。我的同事(顺便说一句,他不在这里)通过编程方式进行了配置,似乎一切正常,除了TransactionManager。我从来没有像这样配置过Spring Web应用程序,也不知道该怎么做,也找不到关于如何配置Web应用程序的任何文档。

我只会展示'AppInitializer'和'EntityManagerConfig'。

AppInitializer:

public class AppInitializer implements WebApplicationInitializer {

@Override
public void onStartup(ServletContext servletContext) throws ServletException {
    AnnotationConfigWebApplicationContext context;
    ServletRegistration.Dynamic dispatcherServletRegistration;
    FilterRegistration.Dynamic encodingFilterRegistration, compressionFilterRegistration;
    Set<SessionTrackingMode> sessionTrackingModes = new HashSet<SessionTrackingMode>();

    sessionTrackingModes.add(SessionTrackingMode.SSL);

    context = new AnnotationConfigWebApplicationContext();
    context.setServletContext(servletContext);
    context.scan("de.devbliss.doc");

    servletContext.addListener(new ContextLoaderListener(context));
    servletContext.addListener(new Log4jConfigListener());

    dispatcherServletRegistration = servletContext.addServlet("main", new DispatcherServlet(context));
    dispatcherServletRegistration.setLoadOnStartup(1);
    dispatcherServletRegistration.addMapping("/*");

    encodingFilterRegistration = servletContext.addFilter("encodingFilter", CharacterEncodingFilter.class);
    encodingFilterRegistration.setInitParameter("encoding", "UTF-8");
    encodingFilterRegistration.setInitParameter("forceEncoding", "true");
    encodingFilterRegistration.addMappingForUrlPatterns(null, false, "/*");

    compressionFilterRegistration = servletContext.addFilter("compressionFilter", GzipFilter.class);
    compressionFilterRegistration.addMappingForUrlPatterns(null, false, "/*");

    compressionFilterRegistration = servletContext.addFilter("springSecurityFilterChain",
            DelegatingFilterProxy.class);
    compressionFilterRegistration.addMappingForUrlPatterns(null, false, "/*");

    servletContext.setSessionTrackingModes(sessionTrackingModes);
}
}

实体管理器配置:

@Configuration
@PropertySource("classpath:/db.properties")
public class EntityManagerConfig {

@Bean
public DataSource dataSource(Environment env) {
    BasicDataSource ds = new BasicDataSource();

    ds.setUrl(env.getProperty("url", "localhost"));
    ds.setUsername(env.getProperty("user", "blissdoc"));
    ds.setPassword(env.getProperty("password", "s3cret"));

    return ds;
}

@Bean
@Inject
public LocalSessionFactoryBean sessionFactory(DataSource dataSource) {
    LocalSessionFactoryBean factory = new LocalSessionFactoryBean();

    factory.setDataSource(dataSource);

    return factory;
}

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

@SuppressWarnings("unchecked")
@Bean
@Inject
public LocalContainerEntityManagerFactoryBean entityManager(
        DataSource dataSource, AbstractEnvironment env) {
    LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
    HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
    HibernateJpaDialect jpaDialect = new HibernateJpaDialect();
    org.springframework.core.env.PropertySource<?> source;
    Iterator<org.springframework.core.env.PropertySource<?>> sources;

    // jpaVendorAdapter.setDatabase(Database.MYSQL);

    jpaVendorAdapter.setGenerateDdl(true);
    jpaVendorAdapter.setShowSql(true);

    Properties jpaProperties = new Properties();

    sources = env.getPropertySources().iterator();
    while (sources.hasNext()) {
        source = sources.next();
        if (source.getSource() instanceof Map) {
            for (Map.Entry<String, String> property : ((Map<String, String>) source
                    .getSource()).entrySet()) {
                jpaProperties.put(property.getKey(), property.getValue());
            }
        }
    }

    em.setJpaProperties(jpaProperties);

    em.setDataSource(dataSource);
    em.setPersistenceUnitName("blissdoc-unit");
    em.setPackagesToScan("de.devbliss.doc.model");
    em.setJpaDialect(jpaDialect);
    em.setJpaVendorAdapter(jpaVendorAdapter);

    return em;
}

// @Bean
// @Inject
// public JpaTransactionManager jpaTransactionManager(
// EntityManagerFactory entityManagerFactory) {
// JpaTransactionManager tm = new JpaTransactionManager(
// entityManagerFactory);
// return tm;
// }

@Bean
@Inject
public JpaRepositoryFactory jpaRepositoryFactory(
        EntityManagerFactory entityManagerFactory) {
    JpaRepositoryFactory factory = new JpaRepositoryFactory(
            entityManagerFactory.createEntityManager());
    return factory;
}

@Bean
@Inject
public UserRepository userRepository(
        JpaRepositoryFactory jpaRepositoryFactory) {
    return jpaRepositoryFactory.getRepository(UserRepository.class);
}

@Bean
@Inject
public ProjectRepository projectRepository(
        JpaRepositoryFactory jpaRepositoryFactory) {
    return jpaRepositoryFactory.getRepository(ProjectRepository.class);
}
}

--- 更新PersistenceJPAConfig(原实体管理器配置):

@Configuration
@EnableTransactionManagement
@PropertySource("classpath:/db.properties")
public class PersistenceJPAConfig {

@Inject
private Environment env;

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
    LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
    factoryBean.setDataSource(dataSource());
    factoryBean.setPackagesToScan(new String[] { "de.devbliss.doc" });
    JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter() {
        {
            // JPA properties
        }
    };
    factoryBean.setJpaVendorAdapter(vendorAdapter);

    return factoryBean;
}

@Bean
public DataSource dataSource() {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setUrl(env.getProperty("url", "localhost"));
    dataSource.setUsername(env.getProperty("user", "blissdoc"));
    dataSource.setPassword(env.getProperty("password", "s3cret"));
    return dataSource;
}

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

    return transactionManager;
}

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

这是TransactionManager的链接,http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html - Arham
这个链接只描述了基于XML配置的TransactionManager,但我必须坚持使用编程配置。 - dwalldorf
本节仅描述编程式事务管理,不包括TransactionManager的编程式配置。此文档部分适用于Spring 2.0版本。据我所知,该版本尚未完全支持编程式配置。 - dwalldorf
据我所记,这些基于注解的配置(它并不是真正的“编程式”)只是除了 XML 之外向 Spring 提供配置的另一种方式。理念应该是相同的,不是吗?那么这个配置为什么“不起作用”呢? - Adrian Shum
目前看来,配置本身似乎是有效的,但Jetty仍然显示“未配置事务管理器”。已在顶部帖子中更新了配置。为什么不应该是编程配置呢? - dwalldorf
显示剩余2条评论
2个回答

6

@Configuration 特定于自定义 XML 元素(例如 <tx:annotation-driven>)的对应项是 @Enable... 注解。

为了启用 @Transactional 支持,您需要在您的 @Configuration 类上添加 @EnableTransactionManagement 注释:

@Configuration 
@PropertySource("classpath:/db.properties") 
@EnableTransactionManagement
public class EntityManagerConfig { ... }

相关阅读:


谢谢,已添加EnableTransactionManagement注释,但Jetty仍然声称未配置事务管理器。并请参见我的更新后的问题与新配置。 - dwalldorf
@entek:就我所记得的,Jetty 的这条消息与 Spring 事务管理无关,你可以忽略它。 - axtavt
好的,假设这条消息来自其他地方并且不重要。所以我创建一个新用户(从我的UserModel中),然后调用userRepository.save(user)。没有抛出错误,但是没有写入到数据库。如果我调用userRepository.flush(),我会得到以下消息:org.springframework.web.util.NestedServletException: Request processing failed; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress - dwalldorf
如果您认为有帮助的话,我可以在原始帖子中发布UserController和UserRepository。 - dwalldorf
并不是真的..问题出在TransactionManager的奇怪行为上。但我们决定接受它并保持原样。 - dwalldorf

4

你尝试过从TransactionManagementConfigurer进行扩展吗?

@Configuration
@EnableTransactionManagement
@PropertySource("classpath:/db.properties")
public class EntityManagerConfig implements TransactionManagementConfigurer {

    ...

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

    @Bean
    public PlatformTransactionManager transactionManager() {
        JpaTransactionManager transactionManager = new JpaTransactionManager(entityManagerFactory());
        transactionManager.setDataSource(dataSource);
        transactionManager.setJpaDialect(new HibernateJpaDialect());
        return transactionManager;
    }

    ...
}

看起来你正在使用Spring Data JPA,所以我建议你也使用@EnableJpaRepositories("com.your.repositories.package")来自动配置你的Spring Data Repositories。

希望这能有所帮助 :)


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