2013-02-10 178 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更好地描述了預期的用途。