2013-02-10 177 views
5

我想创建一个Spring JPA应用程序,利用它的持久层JPA。不幸的是,当访问EntityManager时,我得到了一个N​​ullPointerException,因为Spring似乎没有注入它。我的配置是基于@EnableWebMvc的所有注释。经过一番搜索之后,我在我的@Configuration类中添加了@Transactional对我的DAO和@EnableTransactionManagement。然后我得到一个关于没有DataSource的错误。据推测,具有@EnableTransactionManagement的类需要实现TransactionManagementConfigurer。但是,我在解决如何创建DataSource时遇到问题,以及为什么无法从我的persistence.xml中获取它。JPA与Spring MVC通过注释配置

我非常感谢任何帮助,试图让我的DAO注入EntityManager。

我@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(); 
} 

} 

吾道

@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。从我对所有事物的理解中,似乎AOP后处理方法中发生异常。

我@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; 
} 

} 

回答

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"); 
} 

更多细节,你可以找到in this article。有一个JndiDatasourceConfig类的例子。

编辑2: 我ahve的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配置任何持久单位名称。

+0

这轻微地关心我,你显式引用Hibernate特定的类,因为它似乎将它绑定到底层实现。 此外,理想情况下,我会使用JNDI查找数据源,但不熟悉如何做。我假设这不是这样做的? – Marshmellow1328 2013-02-10 18:57:27

+0

关于绑定到底层实现 - 即使您在没有Spring的情况下使用JPA,也可以在persistence.xml中定义持久性提供程序设置。在这里你可以在一个方法entityManagerFactoryBean()中的代码中定义它们。但你可以将它们移动到XML配置文件) – dimas 2013-02-10 19:06:08

+0

我添加了示例如何在我的原始答案中通过JNDI查找来获取DataSource。 – dimas 2013-02-10 19:13:27

1

下也许会有帮助,尽管它使用基于XML的配置:

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

它使用了Spring数据JPA,但你没有这样做。使用

@PersistenceContext private EntityManager entityManager; 

(但考虑春天JPA的数据,因为它提供了非常有能力的DAO的开箱即用。)

边注:对于DAO的,有利于@Repository超过@Component。两者都适用于组件扫描,但@Repository更好地描述了预期的用途。