2013-02-10 10 views
5

Próbuję utworzyć aplikację Spring MVC wykorzystującą JPA dla jej warstwy trwałości. Niestety, otrzymałem wyjątek NullPointerException podczas uzyskiwania dostępu do EntityManager, ponieważ Spring nie wydaje się go wstrzykiwać. Moja konfiguracja jest oparta na adnotacji z @EnableWebMvc. Po pewnym wyszukiwaniu dodałem @Transactional na moich DAO i @EnableTransactionManagement na mojej klasie @Configuration. Wtedy dostałem błąd dotyczący braku źródła danych. Podobno klasa z @EnableTransactionManagement musi wdrożyć TransactionManagementConfigurer. Mam jednak problemy z ustaleniem, jak utworzyć źródło danych, a także dlaczego nie można go pobrać z pliku persistence.xml.JPA ze Spring MVC Configured via Adnotations

Byłbym wdzięczny za każdą pomoc w próbie wprowadzenia EntityManager do mojego DAO.

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

} 

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

} 

Moja zaktualizowanego kodu

doszedłem do punktu, w którym to prawie wszystkie prace. EntityManager jest prawidłowo wstrzykiwany. Jednak transakcje nie działają. Występują błędy, jeśli próbuję użyć podejścia RESOURCE_LOCAL, więc szukam transakcji zarządzanych przez JTA. Kiedy dodaję @Transactional do każdej z moich metod DAO, pojawia się błąd "Transakcja oznaczona dla przywrócenia", bez żadnych szczegółów w plikach dziennika, aby pomóc w rozwiązywaniu problemów. Jeśli usunę adnotację z podstawowego wyboru tylko do odczytu, wybór będzie działał idealnie dobrze (nie jestem pewien, czy powinienem nawet umieszczać adnotację na metodach tylko do wyboru). Jednak oczywiście potrzebuję tego działa na metodach, które wykonują zapisy db. Jeśli debuguję kod, wydaje się, że dane są idealnie dokładne. Jednak po powrocie z metody generowany jest wyjątek javax.transaction.RollbackException. Z mojego rozumienia wszystkiego wydaje się, że wyjątek występuje w przetwarzaniu końcowym AOP.

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

} 

Odpowiedz

9

w mojej aplikacji nie dostosował interfejs TransactionManagerConfigurer. Używam kolejnego kodu do konfiguracji JPA (z implementacją Hibernate). Możesz zrobić to samo w swojej klasie konfiguracji.

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

Nadzieja to pomoże)

EDIT:

można uzyskać stosując źródło danych JNDI odnośnika:

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

Więcej szczegółów można znaleźć in this article. Jest przykład z klasą JndiDatasourceConfig.

EDIT 2: I ahve persistence.xml w moim projekcie, ale jest pusty:

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

I nie określił żadnego trwałego nazwę jednostki w mojej konfiguracji java.

+0

Łagodnie niepokoi mnie to, że wyraźnie odwołujesz się do klas specyficznych dla Hibernacji, ponieważ wydaje się, że wiąże to z podstawową implementacją. Również najlepiej byłoby użyć wyszukiwania JNDI dla źródła danych, ale nie wiem, jak to zrobić. Zakładam, że to nie robi? – Marshmellow1328

+0

Informacje o powiązaniu z podstawową implementacją - nawet jeśli używasz JPA bez Spring, definiujesz ustawienia dostawcy utrwalania w pliku persistence.xml. Tutaj definiujesz je w kodzie w jednej metodzie entityManagerFactoryBean(). Ale możesz przenieść je do pliku konfiguracyjnego xml) – dimas

+0

Dodałem przykład jak uzyskać DataSource za pośrednictwem wyszukiwania JNDI w mojej oryginalnej odpowiedzi. – dimas

1

Następujące może pomóc, mimo że używa konfiguracji opartej na XML:

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

Wykorzystuje Wiosna danych JPA, ale nie musisz tego robić. Użyj

@PersistenceContext private EntityManager entityManager; 

(Ale rozważyć wiosna danych WZP gdyż zapewnia bardzo zdolnych DAOs po wyjęciu z pudełka.)

marginesie: Dla DAOs sprzyjają @Repository nad @Component. Oba działają na skanowanie komponentów, ale lepiej opisują zamierzone zastosowanie.