EntityManager 坚持并合并在@Transactional 方法中不起作用
EntityManager persist and merge not working in @Transactional method
@Transactional 注释和 EntityManager 持久和合并方法有奇怪的行为。我将方法注释为 @Transactional 并在其中调用 EntityManager.persists(entity)... 但没有任何反应。实体没有保存到数据库,没有触发异常,完全没有。我已经阅读了大量示例、SO 问题,我的代码似乎还可以,但无法正常工作。
休眠配置:
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);
}
Aaaaand...它有效。并且交易状态变为ACTIVE。
所以我的问题是:
- 为什么没有 joinTransaction() 就不能工作?我从来没有在例子中看到它......
-(可能在解决第一个问题后这个问题就没有意义了)持久化和合并实际上是在抽象 dao class 中,但是我有很多实体,很多实体 dao 和很多自定义方法。并且在每个中调用 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'
编辑 2:
Spring version 5.1.7.RELEASE
Hibernate version 5.4.10.Final
javax.persistence:javax.persistence-api:2.2 (dependency from hibernate-core)
终于找到问题所在:我正在从 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;
}
虽然 bean 名称不同,但这是错误的。
将 SF 事务管理器方法更改为 sessionTransactionManager(...) 可解决问题。
@Transactional 注释和 EntityManager 持久和合并方法有奇怪的行为。我将方法注释为 @Transactional 并在其中调用 EntityManager.persists(entity)... 但没有任何反应。实体没有保存到数据库,没有触发异常,完全没有。我已经阅读了大量示例、SO 问题,我的代码似乎还可以,但无法正常工作。
休眠配置:
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);
}
Aaaaand...它有效。并且交易状态变为ACTIVE。
所以我的问题是: - 为什么没有 joinTransaction() 就不能工作?我从来没有在例子中看到它...... -(可能在解决第一个问题后这个问题就没有意义了)持久化和合并实际上是在抽象 dao class 中,但是我有很多实体,很多实体 dao 和很多自定义方法。并且在每个中调用 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'
编辑 2:
Spring version 5.1.7.RELEASE
Hibernate version 5.4.10.Final
javax.persistence:javax.persistence-api:2.2 (dependency from hibernate-core)
终于找到问题所在:我正在从 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;
}
虽然 bean 名称不同,但这是错误的。 将 SF 事务管理器方法更改为 sessionTransactionManager(...) 可解决问题。