2013-02-10 41 views
5

Tôi đang cố gắng tạo ứng dụng Spring MVC tận dụng JPA cho lớp kiên trì của nó. Thật không may, tôi đã nhận được một NullPointerException khi truy cập EntityManager vì Spring dường như không tiêm nó. Cấu hình của tôi là tất cả dựa trên chú thích với @EnableWebMvc. Sau khi tìm kiếm, tôi đã thêm @Transactional vào DAO và @EnableTransactionManagement vào lớp @Configuration của tôi. Sau đó, tôi nhận được một lỗi về việc không có một DataSource. Giả sử, một lớp có @EnableTransactionManagement cần phải thực hiện TransactionManagementConfigurer. Tuy nhiên, tôi đang gặp vấn đề tìm ra cách tạo DataSource cũng như lý do tại sao nó không thể lấy nó từ persistence.xml của tôi.JPA với Spring MVC được định cấu hình qua chú thích

Tôi sẽ đánh giá rất cao bất kỳ trợ giúp nào trong việc cố gắng đưa EntityManager được tiêm vào DAO của tôi.

lớp @Configuration My

@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 My

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

} 

Mã Cập nhật My

Tôi đã đạt đến điểm mà nó hầu như tất cả các công trình. EntityManager đang được tiêm đúng cách. Tuy nhiên, các giao dịch không hoạt động. Tôi gặp lỗi nếu tôi cố gắng sử dụng phương pháp RESOURCE_LOCAL vì vậy tôi đang xem xét các giao dịch được quản lý bởi JTA. Khi tôi thêm @Transactional vào bất kỳ phương thức DAO nào của mình, tôi nhận được lỗi "Giao dịch được đánh dấu để quay lại" mà không có thêm chi tiết nào trong bất kỳ tệp nhật ký nào để hỗ trợ khắc phục sự cố. Nếu tôi xóa chú thích khỏi lựa chọn chỉ đọc cơ bản, lựa chọn sẽ hoạt động hoàn toàn tốt (không chắc chắn liệu tôi có nên đặt chú thích trên các phương thức chỉ chọn) hay không. Tuy nhiên, tôi rõ ràng cần điều này làm việc trên phương pháp thực hiện db viết. Nếu tôi gỡ lỗi thông qua mã, có vẻ như để lấy dữ liệu hoàn toàn tốt. Tuy nhiên, khi nó trả về từ phương thức, javax.transaction.RollbackException bị ném. Từ sự hiểu biết của tôi về tất cả mọi thứ, nó có vẻ như là nếu ngoại lệ xảy ra trong xử lý sau phương pháp AOP.

My @Configuration lớp

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

} 

Trả lời

9

Trong ứng dụng của tôi, tôi đã không thực hiện giao diện TransactionManagerConfigurer. Tôi sử dụng mã tiếp theo để cấu hình JPA (với Hibernate thực hiện). Bạn có thể làm tương tự trong lớp cấu hình của bạn.

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

Hy vọng điều này sẽ giúp bạn)

EDIT:

Bạn có thể lấy nguồn dữ liệu sử dụng tra cứu JNDI:

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

Thông tin chi tiết bạn có thể tìm in this article. Có ví dụ với lớp JndiDatasourceConfig.

EDIT 2: tôi ahve persistence.xml trong dự án của tôi, nhưng nó là rỗng:

<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> 

Và tôi đã không chỉ định bất kỳ tên đơn vị dai dẳng trong cấu hình java của tôi.

+0

Điều này liên quan đến tôi một cách rõ ràng rằng bạn tham chiếu rõ ràng các lớp Hibernate cụ thể vì nó dường như buộc nó vào triển khai cơ bản. Ngoài ra, lý tưởng, tôi muốn sử dụng tra cứu JNDI cho nguồn dữ liệu, nhưng không quen thuộc với cách tôi làm điều đó. Tôi giả định điều này là không làm điều đó? – Marshmellow1328

+0

Về việc buộc phải triển khai cơ bản - ngay cả khi bạn sử dụng JPA mà không có Spring, bạn xác định các thiết lập của nhà cung cấp bền vững trong persistence.xml. Ở đây bạn định nghĩa chúng trong mã trong một phương thức entityManagerFactoryBean(). Nhưng bạn có thể chuyển chúng sang tệp cấu hình xml) – dimas

+0

Tôi đã thêm ví dụ về cách lấy DataSource qua tra cứu JNDI trong câu trả lời ban đầu của tôi. – dimas

1

Sau đây có thể giúp đỡ, mặc dù nó sử dụng cấu hình dựa trên XML:

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

Nó sử dụng mùa xuân dữ liệu JPA, nhưng bạn không cần phải làm điều đó. Sử dụng

@PersistenceContext private EntityManager entityManager; 

(Nhưng xem xét xuân dữ liệu JPA vì nó cung cấp DAO rất có khả năng ra khỏi hộp.)

Side lưu ý: Đối với DAO, ủng hộ @Repository qua @Component. Cả hai đều hoạt động để quét thành phần, nhưng @Repository mô tả tốt hơn mục đích sử dụng.

Các vấn đề liên quan