2013-02-10 2 views
5

Я пытаюсь создать приложение Spring MVC с использованием JPA для его уровня сохранения. К сожалению, я получал исключение NullPointerException при доступе к EntityManager как Spring, похоже, не вводит его. Моя конфигурация основана на аннотациях с помощью @EnableWebMvc. После некоторого поиска я добавил @Transactional в свой DAO и @EnableTransactionManagement в свой класс @Configuration. Затем я получил сообщение об отсутствии источника данных. Предположительно, класс с @EnableTransactionManagement должен реализовать TransactionManagementConfigurer. Однако у меня возникают проблемы с выяснением того, как создать DataSource, а также почему он не может получить его из моего persistence.xml.JPA с Spring MVC, сконфигурированный с помощью аннотаций

Я бы очень признателен за любую помощь в попытке получить EntityManager, введенный в мой DAO.

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

} 

Мой 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; 
    } 

} 

Мой обновленный код

Я достиг точки, в которой он почти все работы. EntityManager вводится правильно. Однако транзакции не работают. Я получаю ошибки, если я пытаюсь использовать подход RESOURCE_LOCAL, поэтому я рассматриваю транзакции, управляемые JTA. Когда я добавляю @Transactional по любому из моих методов DAO, я получаю ошибку «Транзакция, отмеченную для отката», без каких-либо подробностей в любых файлах журналов, чтобы помочь устранить неполадки. Если я удалю аннотацию из основного выбора только для чтения, выбор будет работать отлично (не уверен, что я даже должен помещать аннотацию в методы только для выбора). Однако я, очевидно, нуждаюсь в этом, работая над методами, которые выполняют db-записи. Если я отлаживаю код, он, как представляется, отлично извлекает данные. Однако, поскольку он возвращается из метода, генерируется javax.transaction.RollbackException. Из моего понимания всего, кажется, что исключение происходит в обработке пост-метода АОП.

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

Надеется, что это поможет)

EDIT:

Вы можете получить с помощью источника данных JNDI поиска:

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

Подробнее вы можете найти in this article. Пример с классом JndiDatasourceConfig.

EDIT 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

+0

О привязке к основной реализации - даже если вы используете JPA без пружины, вы определяете настройки поставщика сохранения в файле persistence.xml. Здесь вы определяете их в коде в одном методе entityManagerFactoryBean(). Но вы можете переместить их в файл конфигурации xml) – dimas

+0

Я добавил пример, как получить DataSource через JNDI-поиск в моем первоначальном ответе. – dimas

1

Следующая может помочь, даже если он использует конфигурацию XML на основе:

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

Он использует Spring Data JPA, но вы не должны делать это. Используйте

@PersistenceContext private EntityManager entityManager; 

(Но рассмотрим Spring Data JPA, поскольку она обеспечивает очень способные объекты DAO из коробки.)

Side Примечание: Для Даосы пользу @Repository над @Component. Оба работают для сканирования компонентов, но @Repository лучше описывает предполагаемое использование.

Смежные вопросы