Spring Boot / @Transactional работает в автономном режиме, но не в Glassfish - PullRequest
0 голосов
/ 01 ноября 2018

У меня есть служба (JHipster4), которая использует репозитории JPA для управления базой данных Postgres. Эта служба импортирует CSV и создает множество объектов. Открытый метод, который создает объекты, помечается @Transactional (import org.springframework.transaction.annotation.Transactional)

При запуске автономного приложения (bootRun, OpenJDK 1.8, внутренний сервер Tomcat) импорт выполняется быстро с 700 объектами / с. Как только импорт завершится, я сразу вижу данные, появляющиеся в базе данных. Это позволяет мне быть уверенным, что транзакция работает правильно.

Как только я разверну свое приложение на payara 4.1, аннотация @Transactional, похоже, не будет работать так, как раньше. Я вижу, что база данных заполняется построчно, и скорость низкая (20 объектов / с), как без @ Transactional.

В рамках моего импорта я усекаю таблицы базы данных, используя entityManager и createNativeQuery (..). ExecuteUpdate ();

Если я удалю @Transactional, я получу

javax.persistence.TransactionRequiredException: Executing an update/delete

что хорошо. На Glassfish с @Transactional я не получаю такую ​​ошибку (что тоже хорошо), но не похоже, что с точки зрения базы данных выполняется транзакция (объекты медленно заполняются).

Конфигурация Payara полностью по умолчанию. Конфигурация базы данных Spring также минимальна, com.zaxxer.hikari.HikariDataSource, база данных платформы: io.github.jhipster.domain.util.FixedPostgreSQL82Dialect, база данных: POSTGRESQL.

в файле beans.xml я установил bean-discovery-mode = "none", потому что CDI доставил мне проблемы.

Так в чем же основное отличие использования Spring Boot JPA в автономном контексте и в контейнере приложений (glassfish)?

Почему @Transactional работает не так, как в автономной версии? Можете ли вы объяснить, что происходит и как решить?

Заранее спасибо!

1 Ответ

0 голосов
/ 29 ноября 2018

При работе в среде сервера приложений сервер приложений предоставляет EntityManager приложению. При автономной работе EntityManager приходит с весны. Это означает, что если Приложение работает в Glassfish, Glassfish управляет транзакциями.

Поскольку менеджер транзакций Glassfisch не понимает аннотацию Spring @Transactional, он не оказывает влияния.

Вариант 1 - изменить код для использования API транзакций серверов приложений. я не хотел этого делать, потому что потерял бы способность работать автономно (т. е. для тестов).

Вариант 2 - реализовать свои собственные EntityManager, TransactionManager и DataSource, как это. Моей целью было создать замену по умолчанию TransactionManager, который считывает конфигурацию весенней загрузки.

@Configuration
public class EntityManagerConfiguration {

    protected final static Logger log = LoggerFactory.getLogger(EntityManagerConfiguration.class);

    private final ConfigurableEnvironment env;

    @Autowired
    public EntityManagerConfiguration(ConfigurableEnvironment env) {
        this.env = env;
    }

    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean entityManager() {

        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource());
        em.setPackagesToScan(
                "com.project.domain"
        );

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        HashMap<String, Object> properties = new HashMap<>();
        properties.put("hibernate.physical_naming_strategy", SpringPhysicalNamingStrategy.class.getName());
        properties.put("hibernate.implicit_naming_strategy", SpringImplicitNamingStrategy.class.getName());
        Map<String,Object> props = ConfigAccessService.getPropertiesStartingWith(
                env,
                "spring.jpa.properties"
        );

        for(Map.Entry<String,Object> config : props.entrySet()) {
            String configName = config.getKey().replace("spring.jpa.properties.","");
            properties.put(configName, config.getValue());
            log.info("setting " + configName + " = " + config.getValue());
        }
        em.setJpaPropertyMap(properties);
        log.info("EntitiyManager configured");

        return em;
    }

    /**
     * as we want to run stand-alone (tests) and in glassfish(JavaEE) the same, we setup our own connection
     * using the Hikari Connection-Pooling.
     * @return
     */
    @Primary
    @Bean
    public DataSource dataSource(){
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setDriverClassName("org.postgresql.Driver");
        hikariConfig.setJdbcUrl(env.getProperty("spring.datasource.url"));
        hikariConfig.setUsername(env.getProperty("spring.datasource.username"));
        hikariConfig.setPassword(env.getProperty("spring.datasource.password"));

        hikariConfig.setMaximumPoolSize(10);
        hikariConfig.setConnectionTestQuery("SELECT 1");
        hikariConfig.setPoolName("springHikariCP");

        HikariDataSource dataSource = new HikariDataSource(hikariConfig);

        return dataSource;
    }

    @Primary
    @Bean
    public PlatformTransactionManager transactionManager() {

        JpaTransactionManager transactionManager
                = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(
                entityManager().getObject());
        return transactionManager;
    }


}
...