EntityManager сохранится и слияние не работает в методе @Transactional - PullRequest
0 голосов
/ 21 февраля 2020

У меня странное поведение аннотации @Transactional и методов сохранения и слияния EntityManager. Я аннотировал метод как @Transactional и в нем я вызываю EntityManager.persists (entity) ... и ничего не происходит. Сущность не сохраняется в БД, без исключений, абсолютно ничего. Я прочитал множество примеров, ТАК вопросы, и мой код выглядит нормально, но не работает.

Конфигурация Hibernate:

import org.hibernate.jpa.HibernatePersistenceProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.jndi.JndiTemplate;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.naming.NamingException;
import javax.sql.DataSource;
import java.sql.Connection;
import java.util.Properties;

@Configuration
@EnableTransactionManagement
@ComponentScan(basePackages = {"com.mycompany"})
@DependsOn({"PropertiesConfig"})
public class HibernateConfig {

    public HibernateConfig() {}

    @Bean
    public DataSource dataSource() throws NamingException {
        return (DataSource) new JndiTemplate().lookup(PropertyService.getInstance().getProperty("jndi.jdbc.AGSQL"));
    }

    @Bean(name = "entityManager")
    @DependsOn("dataSource")
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException {
        System.out.println("*** Init EntityManagerFactory ***");
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setPersistenceProviderClass(HibernatePersistenceProvider.class);
        em.setDataSource(dataSource());
        em.setPackagesToScan(new String[] { "com.mycompany" });
        em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        em.setJpaProperties(hibernateProperties());
        return em;
    }

    @Bean(name = "tm")
    public PlatformTransactionManager transactionManager() throws NamingException {
        System.out.println("*** Init TransactionManager ***");
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
        return transactionManager;
    }

    @Bean
    public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
        return new PersistenceExceptionTranslationPostProcessor();
    }

    private Properties hibernateProperties() {
        return new Properties() {
            {
                setProperty("hibernate.dialect", PropertyService.getInstance().getProperty("hibernate.dialect"));
                setProperty("show_sql", PropertyService.getInstance().getProperty("show_sql"));
                setProperty("hibernate.connection.isolation", String.valueOf(Connection.TRANSACTION_READ_UNCOMMITTED));
            }
        };
    }
}

Объект:

import org.hibernate.annotations.UpdateTimestamp;    
import javax.persistence.*;
import java.time.LocalDateTime;

@Entity
@Table(name = "Vehicle", schema = "dbo")
public class VehicleEntity {
    private Long deviceId;
    private LocalDateTime dtEdit;
    private String name;

    @Id
    @Column(name = "device_id")
    public Long getDeviceId() {
        return deviceId;
    }

    public void setDeviceId(Long deviceId) {
        this.deviceId = deviceId;
    }

    @Basic
    @Column(name = "dt_edit")
    @UpdateTimestamp
    public LocalDateTime getDtEdit() {
        return dtEdit;
    }

    public void setDtEdit(LocalDateTime dtEdit) {
        this.dtEdit = dtEdit;
    }

    @Basic
    @Column(name = "name")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

Дао :

import com.mycompany.VehicleEntity;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Repository
public class VehicleDao {

    @PersistenceContext(unitName = "entityManager")
    private EntityManager entityManager;

    protected EntityManager getEntityManager() {
        return entityManager;
    }

    @Transactional(transactionManager = "tm")
    public void persist(VehicleEntity entity) {
        getEntityManager().persist(entity);
    }

    @Transactional("tm")
    public void merge(VehicleEntity entity) {
        getEntityManager().merge(entity);
    }

}

И выполнение этого кода абсолютно ничего не делает:

VehicleEntity ve = new VehicleEntity();
ve.setDeviceId(111L);
ve.setName("111");
vehicleDao.persist(ve);

Я пытался проверить статус транзакции внутри persist метод:

((Session)this.getEntityManager().getDelegate()).getTransaction().getStatus() = NOT_ACTIVE

Итак, я могу сделать вывод, что транзакция не началась или не началась, но entitymanager не увидел ее автоматически (см. Ниже). В одном из вопросов я увидел, что перед вызовом persist (entity) менеджер сущностей присоединился к транзакции и сделал то же самое:

@Transactional(transactionManager = "tm")
public void persist(T entity) {
    getEntityManager().joinTransaction();
    getEntityManager().persist(entity);
}

Aaaa и ... это работает. И статус транзакции становится АКТИВНЫМ.

Итак, мои вопросы: - почему он не работал без joinTransaction () ? Я никогда не видел это в примерах ... - (возможно, после решения 1-го вопроса этот вопрос не будет иметь смысла) сохранение и слияние фактически происходят в абстрактном дао-классе, но у меня есть много сущностей, много сущностей дао и много пользовательских методов в них , И вызывать joinTransaction () в каждом из них не очень хорошая идея. Как сделать это красивым способом?

РЕДАКТИРОВАТЬ:

Проверено еще одно - добавлено свойство распространения:

@Transactional(transactionManager = "tm", propagation = Propagation.MANDATORY)
public void persist(T entity) {
    getEntityManager().persist(entity);
}

И выдает исключение:

org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'

EDIT2:

Spring version 5.1.7.RELEASE 
Hibernate version 5.4.10.Final
javax.persistence:javax.persistence-api:2.2 (dependency from hibernate-core)

1 Ответ

0 голосов
/ 27 февраля 2020

Наконец обнаружил проблему: я мигрировал из SessionFactory в EntityManager и оставил в конфигурационном файле старые настройки для SessionFactory. И проблема была в том, что менеджеры транзакций имеют одинаковые имена методов для SF и EM:

@Bean(name = "transactionManager")
public PlatformTransactionManager transactionManager(SessionFactory sessionFactory) {
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(sessionFactory);
    return transactionManager;
}

@Bean(name = "tm")
public PlatformTransactionManager transactionManager(@Qualifier("customEntityManager") EntityManagerFactory emf) {
    System.out.println("*** Init TransactionManager ***");
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(emf);
    return transactionManager;
}

Это ошибка, хотя имена бинов разные. Изменение метода диспетчера транзакций SF на sessionTransactionManager (...) решает проблему.

...