EntityManager.persist() 不适用于 @Transactional

EntityManager.persist() not working with @Transactional

提问人:Gaetan L. 提问时间:9/22/2023 最后编辑:Ken ChanGaetan L. 更新时间:9/24/2023 访问量:414

问:

我正在尝试学习如何在 Spring 中使用@Transactional注解(我没有使用 Spring Boot)。但是当我使用 begin() 和 commit() 从我的 - 工作 - 方法中抽离出带有 @Transactional 的方法时,我的实体不会持久化。你能帮我了解我做错了什么吗?

无事务/工作方法:

public <S extends Message> S save(S entity) {
    EntityManager em = pu.getEntityManager();
    try {
        logger.debug("Trying to save " + ((Message) entity).toString());
        em.getTransaction().begin();
        em.persist(entity);
        em.getTransaction().commit();
        logger.debug("MessageRepository.save() - after .commit()");
    }
    catch (PersistenceException e) {
        logger.error("Entity already exists");
        e.printStackTrace();
    }
    finally {
        System.out.println();
        em.close();
    }

    return entity;
}

@Transactional/不工作:

@Transactional
@Override
public <S extends Message> S save(S entity) {
    EntityManager em = pu.getEntityManager();
    em.persist(entity);
    return entity;
}

这是我的 PersustanceUtil (pu) 类:

package com.cypherf.repository;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.annotation.PreDestroy;
import org.hibernate.SessionFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

@Configuration
@Service
public class PersistenceUtil {
    private static EntityManagerFactory emf = null;

    @Bean
    public PlatformTransactionManager txManager() {
        return new JpaTransactionManager(getEntityManagerFactory());
    }

    /**
     * Creates entity manager factory as singleton instance and returns it
     *
     * @return the EntityManagerFactory
     */
    public EntityManagerFactory getEntityManagerFactory() {
        if (emf == null) {
            synchronized (EntityManagerFactory.class) {
                if (emf == null) {
                    final StandardServiceRegistry sr = new StandardServiceRegistryBuilder()
                            .configure() // Configures setting from hibernate.cfg.xml
                            .build();
                    try {
                        emf = new MetadataSources(sr).buildMetadata().buildSessionFactory();
                    }
                    catch (Exception e) {
                        StandardServiceRegistryBuilder.destroy(sr);
                        throw e;
                    }
                }
            }
        }
        return emf;
    }

    /**
     * Closes the entity manager factory
     */
    @PreDestroy
    public static void closeEntityManagerFactory() {
        System.out.println("PersistenceUtil.closeEntityManagerFactory()");
        if (emf != null) {
            System.out.println("Closing emf");
            emf.close();
        }
    }

    /**
     * Returns a new EntityManager instance
     *
     * @return the new EntityManager instance
     */
    public EntityManager getEntityManager() {
        return getEntityManagerFactory().createEntityManager();
    }
}

主类:

package com.cypherf;
import com.cypherf.model.Message;
import com.cypherf.repository.MessageRepository;
import com.cypherf.service.MessageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.repository.CrudRepository;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@ComponentScan
@EnableTransactionManagement
public class TestSpringApplication {
    @Autowired
    private ApplicationContext context;

    @Autowired
    private CrudRepository<Message, Long> messageRepository;

    @Autowired
    private MessageService messageService;

    final static Logger logger = LoggerFactory.getLogger(TestSpringApplication.class);

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(TestSpringApplication.class);
        TestSpringApplication app = ctx.getBean(TestSpringApplication.class);
        logger.info("Running app...");
        app.run(args);
        //TestSpringApplication app = context.getBean(TestSpringApplication.class);
        //TestSpringApplication app = new TestSpringApplication();
        //app.run(args);

    }

    public void run(String... args) {
        messageRepository.save(new Message("Message one"));
        messageRepository.save(new Message("Message two"));
        messageService.printAllMessages();

        messageRepository.save(new Message("Message two"));
        messageService.printAllMessages();

//        System.out.println("BEANS:");
//        List<String> beans = Arrays.asList(context.getBeanDefinitionNames());
//        beans.forEach(bean -> System.out.println(bean));
    }
}

输出如下:

[...]
2419 [main] DEBUG org.hibernate.event.internal.EntityCopyObserverFactoryInitiator  - Configured EntityCopyObserver strategy: disallow
2518 [main] DEBUG org.hibernate.boot.internal.ClassLoaderAccessImpl  - Not known whether passed class name [com.cypherf.model.Message] is safe
2518 [main] DEBUG org.hibernate.boot.internal.ClassLoaderAccessImpl  - No temp ClassLoader provided; using live ClassLoader for loading potentially unsafe class : com.cypherf.model.Message
2790 [main] DEBUG org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl  - HHH000513: Unable to create the ReflectionOptimizer for [com.cypherf.model.Message]: private accessor [text]
2853 [main] DEBUG org.hibernate.orm.model.mapping.creation  - Starting post-init callbacks
2853 [main] DEBUG org.hibernate.orm.model.mapping.creation  - Starting PostInitCallbackEntry : Entity(com.cypherf.model.Message) `staticFetchableList` generator
2853 [main] DEBUG org.hibernate.orm.model.mapping.creation  - Starting PostInitCallbackEntry : Entity(com.cypherf.model.Message) `sqmMultiTableInsertStrategy` interpretation
2946 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  - Static SQL for entity: com.cypherf.model.Message
2946 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Version select: select id from Message where id=?
2946 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Insert (0): insert into Message (text,id) values (?,?)
2946 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Update (0): update Message set text=? where id=?
2946 [main] DEBUG org.hibernate.persister.entity.AbstractEntityPersister  -  Delete (0): delete from Message where id=?
2962 [main] DEBUG org.hibernate.orm.sql.ast.create  - Created new SQL alias : m1_0
2962 [main] DEBUG org.hibernate.orm.sql.ast.create  - Registration of TableGroup [StandardTableGroup(com.cypherf.model.Message)] with identifierForTableGroup [com.cypherf.model.Message] for NavigablePath [com.cypherf.model.Message] 
2993 [main] DEBUG org.hibernate.orm.results.graph.AST  - DomainResult Graph:
 \-EntityResultImpl [com.cypherf.model.Message]
 |  \-BasicFetch [com.cypherf.model.Message.text]

2993 [main] DEBUG org.hibernate.orm.sql.ast.tree  - SQL AST Tree:
    SelectStatement {
      FromClause {
        StandardTableGroup (m1 : com.cypherf.model.Message) {
          primaryTableReference : Message as m1_0
        }
      }
    }

3040 [main] DEBUG org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator  - No JtaPlatform was specified, checking resolver
3040 [main] DEBUG org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformResolverInitiator  - No JtaPlatformResolver was specified, using default [org.hibernate.engine.transaction.jta.platform.internal.StandardJtaPlatformResolver]
3056 [main] DEBUG org.hibernate.engine.transaction.jta.platform.internal.StandardJtaPlatformResolver  - Could not resolve JtaPlatform, using default [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
3056 [main] INFO  org.hibernate.engine.transaction.jta.platform.internal.JtaPlatformInitiator  - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
3056 [main] DEBUG org.hibernate.type.spi.TypeConfiguration$Scope  - Scoping TypeConfiguration [org.hibernate.type.spi.TypeConfiguration@4d27d9d] to SessionFactoryImplementor [org.hibernate.internal.SessionFactoryImpl@3a209918]
3056 [main] DEBUG org.hibernate.query.named.NamedObjectRepository  - Checking 0 named HQL queries
3056 [main] DEBUG org.hibernate.query.named.NamedObjectRepository  - Checking 0 named SQL queries
3071 [main] DEBUG org.hibernate.SQL  - 
    drop table if exists Message cascade 
Hibernate: 
    drop table if exists Message cascade 
3087 [main] INFO  org.hibernate.orm.connections.access  - HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@5170bc02] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode.
3088 [main] DEBUG org.hibernate.SQL  - 
    drop sequence if exists Message_SEQ
Hibernate: 
    drop sequence if exists Message_SEQ
3088 [main] DEBUG org.hibernate.SQL  - 
    create sequence Message_SEQ start with 1 increment by 50
Hibernate: 
    create sequence Message_SEQ start with 1 increment by 50
3088 [main] INFO  org.hibernate.orm.connections.access  - HHH10001501: Connection obtained from JdbcConnectionAccess [org.hibernate.engine.jdbc.env.internal.JdbcEnvironmentInitiator$ConnectionProviderJdbcConnectionAccess@4601047] for (non-JTA) DDL execution was not in auto-commit mode; the Connection 'local transaction' will be committed and the Connection will be set into auto-commit mode.
3103 [main] DEBUG org.hibernate.SQL  - 
    create table Message (
        id bigint not null,
        text varchar(255) unique,
        primary key (id)
    )
Hibernate: 
    create table Message (
        id bigint not null,
        text varchar(255) unique,
        primary key (id)
    )
3119 [main] DEBUG org.hibernate.internal.SessionFactoryRegistry  - Initializing SessionFactoryRegistry : org.hibernate.internal.SessionFactoryRegistry@40dd552c
3119 [main] DEBUG org.hibernate.internal.SessionFactoryRegistry  - Registering SessionFactory: 0c3c67af-87cc-4b0a-bfd9-a5e6498e66fc (<unnamed>)
3119 [main] DEBUG org.hibernate.internal.SessionFactoryRegistry  - Not binding SessionFactory to JNDI, no JNDI name configured
3119 [main] DEBUG org.hibernate.internal.SessionFactoryImpl  - Instantiated SessionFactory
3176 [main] DEBUG org.hibernate.stat.internal.StatisticsInitiator  - Statistics initialized [enabled=false]
3333 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory  - Creating shared instance of singleton bean 'messageService'
3333 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory  - Creating shared instance of singleton bean 'txManager'
3402 [main] INFO  com.cypherf.TestSpringApplication  - Running app...
3409 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager  - Creating new transaction with name [com.cypherf.repository.MessageRepository.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
3409 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager  - Opened new EntityManager [SessionImpl(1809269661<open>)] for JPA transaction
3409 [main] DEBUG org.hibernate.engine.transaction.internal.TransactionImpl  - On TransactionImpl creation, JpaCompliance#isJpaTransactionComplianceEnabled == false
3409 [main] DEBUG org.hibernate.engine.transaction.internal.TransactionImpl  - begin
3409 [main] DEBUG com.cypherf.repository.MessageRepository  - Trying to save Message [id: 0; text:"Message one"]
3425 [main] DEBUG org.hibernate.SQL  - 
    select
        next value for Message_SEQ
Hibernate: 
    select
        next value for Message_SEQ
3425 [main] DEBUG org.hibernate.id.enhanced.SequenceStructure  - Sequence value obtained: 1
3440 [main] DEBUG org.hibernate.event.internal.AbstractSaveEventListener  - Generated identifier: 1, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator
3440 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager  - Initiating transaction commit
3440 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager  - Committing JPA transaction on EntityManager [SessionImpl(1809269661<open>)]
3440 [main] DEBUG org.hibernate.engine.transaction.internal.TransactionImpl  - committing
3456 [main] DEBUG org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl  - Initiating JDBC connection release from afterTransaction
3456 [main] DEBUG org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl  - Initiating JDBC connection release from afterTransaction
3456 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager  - Closing JPA EntityManager [SessionImpl(1809269661<open>)] after transaction
3456 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager  - Creating new transaction with name [com.cypherf.repository.MessageRepository.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
3456 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager  - Opened new EntityManager [SessionImpl(1553616699<open>)] for JPA transaction
3456 [main] DEBUG org.hibernate.engine.transaction.internal.TransactionImpl  - On TransactionImpl creation, JpaCompliance#isJpaTransactionComplianceEnabled == false
3456 [main] DEBUG org.hibernate.engine.transaction.internal.TransactionImpl  - begin
3456 [main] DEBUG com.cypherf.repository.MessageRepository  - Trying to save Message [id: 0; text:"Message two"]
3456 [main] DEBUG org.hibernate.SQL  - 
    select
        next value for Message_SEQ
Hibernate: 
    select
        next value for Message_SEQ
3456 [main] DEBUG org.hibernate.id.enhanced.SequenceStructure  - Sequence value obtained: 51
3456 [main] DEBUG org.hibernate.event.internal.AbstractSaveEventListener  - Generated identifier: 2, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator
3456 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager  - Initiating transaction commit
3456 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager  - Committing JPA transaction on EntityManager [SessionImpl(1553616699<open>)]
3456 [main] DEBUG org.hibernate.engine.transaction.internal.TransactionImpl  - committing
3456 [main] DEBUG org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl  - Initiating JDBC connection release from afterTransaction
3456 [main] DEBUG org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl  - Initiating JDBC connection release from afterTransaction
3456 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager  - Closing JPA EntityManager [SessionImpl(1553616699<open>)] after transaction
3456 [main] INFO  com.cypherf.service.MessageService  - MessageService.printAllMessages()
3456 [main] DEBUG org.hibernate.orm.query.hql  - HQL : from Message
3724 [main] DEBUG org.hibernate.orm.query.sqm.ast  - SqmStatement Tree :
    -> [select]
      -> [query-spec]
        -> [select]
          -> [selection]
            -> [root] - `com.cypherf.model.Message(136820536837300)`
            <- [root] - `com.cypherf.model.Message(136820536837300)`
          <- [selection]
        <- [select]
        -> [from]
          -> [root] - `com.cypherf.model.Message(136820536837300)`
          <- [root] - `com.cypherf.model.Message(136820536837300)`
        <- [from]
      <- [query-spec]
    <- [select]

3771 [main] DEBUG org.hibernate.orm.sql.ast.create  - Created new SQL alias : m1_0
3771 [main] DEBUG org.hibernate.orm.sql.ast.create  - Registration of TableGroup [StandardTableGroup(com.cypherf.model.Message(136820536837300))] with identifierForTableGroup [com.cypherf.model.Message] for NavigablePath [com.cypherf.model.Message] 
3787 [main] DEBUG org.hibernate.orm.results.graph.AST  - DomainResult Graph:
 \-EntityResultImpl [com.cypherf.model.Message(136820536837300)]
 |  \-BasicFetch [com.cypherf.model.Message(136820536837300).text]

3787 [main] DEBUG org.hibernate.orm.sql.ast.tree  - SQL AST Tree:
    SelectStatement {
      FromClause {
        StandardTableGroup (m1 : com.cypherf.model.Message(136820536837300)) {
          primaryTableReference : Message as m1_0
        }
      }
    }

3803 [main] DEBUG org.hibernate.orm.sql.exec  - Skipping reading Query result cache data: cache-enabled = false, cache-mode = NORMAL
3819 [main] DEBUG org.hibernate.orm.results  - Initializer list
3819 [main] DEBUG org.hibernate.orm.results  -     com.cypherf.model.Message(136820536837300) -> EntityResultInitializer(com.cypherf.model.Message(136820536837300))@107577149 (SingleTableEntityPersister(com.cypherf.model.Message))
3819 [main] DEBUG org.hibernate.SQL  - 
    select
        m1_0.id,
        m1_0.text 
    from
        Message m1_0
Hibernate: 
    select
        m1_0.id,
        m1_0.text 
    from
        Message m1_0
3819 [main] DEBUG org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl  - Initiating JDBC connection release from afterTransaction
3819 [main] DEBUG org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl  - Initiating JDBC connection release from afterTransaction
3819 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager  - Creating new transaction with name [com.cypherf.repository.MessageRepository.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
3819 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager  - Opened new EntityManager [SessionImpl(225465790<open>)] for JPA transaction
3819 [main] DEBUG org.hibernate.engine.transaction.internal.TransactionImpl  - On TransactionImpl creation, JpaCompliance#isJpaTransactionComplianceEnabled == false
3819 [main] DEBUG org.hibernate.engine.transaction.internal.TransactionImpl  - begin
3819 [main] DEBUG com.cypherf.repository.MessageRepository  - Trying to save Message [id: 0; text:"Message two"]
3834 [main] DEBUG org.hibernate.event.internal.AbstractSaveEventListener  - Generated identifier: 3, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator
3834 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager  - Initiating transaction commit
3834 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager  - Committing JPA transaction on EntityManager [SessionImpl(225465790<open>)]
3834 [main] DEBUG org.hibernate.engine.transaction.internal.TransactionImpl  - committing
3834 [main] DEBUG org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl  - Initiating JDBC connection release from afterTransaction
3834 [main] DEBUG org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl  - Initiating JDBC connection release from afterTransaction
3834 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager  - Closing JPA EntityManager [SessionImpl(225465790<open>)] after transaction
3834 [main] INFO  com.cypherf.service.MessageService  - MessageService.printAllMessages()
3834 [main] DEBUG org.hibernate.orm.sql.exec  - Skipping reading Query result cache data: cache-enabled = false, cache-mode = NORMAL
3834 [main] DEBUG org.hibernate.orm.results  - Initializer list
3834 [main] DEBUG org.hibernate.orm.results  -     com.cypherf.model.Message(136820536837300) -> EntityResultInitializer(com.cypherf.model.Message(136820536837300))@1174086484 (SingleTableEntityPersister(com.cypherf.model.Message))
3834 [main] DEBUG org.hibernate.SQL  - 
    select
        m1_0.id,
        m1_0.text 
    from
        Message m1_0
Hibernate: 
    select
        m1_0.id,
        m1_0.text 
    from
        Message m1_0
3834 [main] DEBUG org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl  - Initiating JDBC connection release from afterTransaction
3834 [main] DEBUG org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl  - Initiating JDBC connection release from afterTransaction

Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.

You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.

For more on this, please refer to https://docs.gradle.org/8.3/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.

BUILD SUCCESSFUL in 5s
3 actionable tasks: 2 executed, 1 up-to-date
13:29:27: Execution finished ':TestSpringApplication.main()'.
java jpa -事务 spring-orm

评论

1赞 Jorn 9/22/2023
也许在交易可以提交之前不要关闭EntityManager
0赞 wi2ard 9/22/2023
你如何调用 save() 方法?(顺便说一句,不要在你的方法中做;你甚至并不总是需要,在活动事务中实例化的实体会在事务结束时自动保留)em.closeem.persist
0赞 Gaetan L. 9/22/2023
@Jorn抱歉,我在删除 .close() 之前复制粘贴了,但我这样做了,但它仍然不起作用。
0赞 Gaetan L. 9/22/2023
@wi2ard,我添加了我调用 save 的 Main 类。是的,我删除了 .close()。我应该使用其他东西而不是persist()吗?
0赞 Chris 9/22/2023
这仅在您自己管理事务时有效,因为您使用的是本地 EntityManager 上下文。通过调用 pu.getEntityManager();,它不受容器管理 - 您可以使用 isJoinedToTransaction() 来检查它是否在活动的 JTA 事务中。如果它正确地挂接到Spring的配置中,你也可以调用joinTransaction()让它注册到活动的JTA事务中,但你最好让Spring直接注入你的EntityManager上下文。

答:

0赞 wi2ard 9/22/2023 #1

查看本指南,了解有关 Spring 事务性的深入而清晰的解释:

您的 UserService(在您的示例中使用 main 是 CrudRepository)会动态代理,并且代理会为您管理事务。但是,处理所有这些事务状态(打开、提交、关闭)的不是代理本身,而是代理委托给事务管理器。

您应该实例化事务管理器 bean,用于 eg。JpaTransactionManager 重用 entityManagerFactory。

我看到您已经设置了一些日志框架,因此您可以启用事务处理的日志记录,以更好地了解正在发生的事情。

评论后编辑

当我将其他保存方法与 begin 和 commit 而不是 Transactional 一起使用时,它会正确记录它们

你可以看看你的潮红。由于您使用的是默认刷新策略,因此 Jpa 将仅在需要时进行刷新 - 即进行需要最新记录的新查询时

评论

0赞 Gaetan L. 9/22/2023
我将日志添加到我的帖子中,但我没有得到任何错误,从我阅读的内容来看,一切都正确发生,当我使用 getAll/printAllMessages 时,数据库中没有任何内容。(当我不使用@Transactional时)
0赞 wi2ard 9/22/2023
一定是您的服务方法 printAllMessages 出了问题,日志显示您的实体已持久化到数据库中
0赞 Gaetan L. 9/22/2023
当我将其他保存方法与 begin 和 commit 而不是 Transactional 一起使用时,它会正确记录它们
0赞 Gaetan L. 9/22/2023
顺便说一句,我正在用我的 emf 实例化 JpaTransactionManager。
0赞 Ken Chan 9/24/2023 #2

这是因为在不起作用的示例中,用于持久化实体的实例与用于开始事务的实例不同。entityManagerentityManager

支持该方法的方面将在实际执行该方法之前自动创建一个并启动该事务。您可以认为以下代码在真正执行该方法之前,实际上在此事务方面幕后执行:@TransactionalentityManager@Transactional@Transactional

EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();

在你的方法中,你需要使用 来访问在事务方面创建的实例,但不要自己创建另一个新的实例,你永远不会在它上面启动任何事务,因此你坚持的实体永远不会得到提交。@Transactional@PersistenceContextentityManagerentityManager

Codewise ,你必须做一些类似的事情:

@Repository
public class SomeRepository {

    @PersistenceContext
    private EntityManager em;
    
    @Transactional
    public <S extends Message> S save(S entity) {;
        em.persist(entity);
        return entity;
    }
}

但是在制作可以正确注入之前,你必须首先通过定义为弹簧豆来正确地将弹簧与休眠物集成。推荐选项是 use(有关详细信息,请参阅此处)。@PersistenceContextentityManagerEntityManagerFactoryLocalContainerEntityManagerFactoryBean

一个快速入门的例子是:

@Bean
public LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean(DataSource ds) {
      LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
      factory.setDataSource(ds);
      factory.setPersistenceProviderClass(HibernatePersistenceProvider.class);

      // configure your factory here ..
      return factory;
}

你只需要被定义为:JpaTransactionManager

@Bean
public PlatformTransactionManager txManager() {
        return new JpaTransactionManager();
}

它将从 spring 上下文(即由 创建的上下文)中检索并使用它。但不使用不受 Spring 管理的那个。EntityManagerFactoryLocalContainerEntityManagerFactoryBean

评论

0赞 Gaetan L. 9/24/2023
当我这样做时,我收到以下错误:由以下原因引起:org.springframework.beans.factory.BeanCreationException:创建名为“messageRepository”的 bean 时出错:注入持久性依赖项失败
0赞 Ken Chan 9/24/2023
好。请看我的更新。您需要先正确设置 hibernate 和 spring 之间的集成。关键是要用 .如果您使用的是spring-boot,则这些内容将由 自动配置。但是由于您现在没有使用 spring-boot ,您需要先手动配置它。LocalContainerEntityManagerFactoryBean
0赞 Gaetan L. 9/24/2023
你能告诉我 DataSource 类来自哪里吗(我需要什么依赖项?
0赞 Ken Chan 9/24/2023
数据源问题似乎与此[问题]有关(stackoverflow.com/questions/77167096/...)?你也会考虑为这个问题提供赏金吗?如果有,我很乐意回答这个问题.干杯
0赞 Gaetan L. 9/24/2023
不幸的是,它还没有资格,我必须等待 2 天。
1赞 Anish B. 9/24/2023 #3

我终于通过修复您的代码使您的代码在我的本地工作。:)

现在,它正在将数据保存在数据库中。我使用mysql数据库进行测试。

对于测试,休眠.cfg.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
    <session-factory>
        <property name="show_sql">true</property>
        <property name="format_sql">true</property>
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/test</property>
        <property name="connection.username">root</property>
        <property name="connection.password">Anish@123</property>
        <property name="hibernate.hbm2ddl.auto">create-drop</property>
        <property name="hibernate.show_sql">true</property>
        <mapping class="com.example.demo.Message"/>
    </session-factory>
</hibernate-configuration>

MessageRepository 类中删除,放入方法并放入 entityManager 而不是:@EnableTransactionManagement@Transactional(transactionManager="txManager")save@PersistenceContext@Autowired

package com.example.demo;

import jakarta.persistence.EntityManager;
import jakarta.persistence.NoResultException;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.repository.CrudRepository;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@Repository
public class MessageRepository implements CrudRepository<Message, Long> {

    @PersistenceContext
    private EntityManager em;

    private final static Logger logger = LoggerFactory.getLogger(MessageRepository.class);

    @Transactional(transactionManager = "txManager")
    @Override
    public @NonNull <S extends Message> S save(@NonNull S message) {
        //em.getTransaction().begin();
        em.persist(message);
        //em.getTransaction().commit();
        return message;
    }

     ....
}

添加 PersistenceUtil 和 EntityManagerFactory,以便 Spring 能够找到 bean 并通过以下方式注入 EntityManager@EnableTransactionManagement@Bean@PersistenceContext

package com.example.demo;

import jakarta.annotation.PreDestroy;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableTransactionManagement
@Service
public class PersistenceUtil {
    final static Logger logger = LoggerFactory.getLogger(PersistenceUtil.class);

    private static EntityManagerFactory emf = null;

    @Bean
    public PlatformTransactionManager txManager() {
        return new JpaTransactionManager(getEntityManagerFactory());
    }

    @Bean
    public EntityManagerFactory getEntityManagerFactory() {
        if (emf == null) {
            synchronized (EntityManagerFactory.class) {
                if (emf == null) {
                    final StandardServiceRegistry sr = new StandardServiceRegistryBuilder()
                            .configure() // Configures setting from hibernate.cfg.xml
                            .build();
                    try {
                        emf = new MetadataSources(sr).buildMetadata().buildSessionFactory();
                    }
                    catch (Exception e) {
                        StandardServiceRegistryBuilder.destroy(sr);
                        throw e;
                    }
                }
            }
        }
        return emf;
    }

    @PreDestroy
    public static void closeEntityManagerFactory() {
        System.out.println("PersistenceUtil.closeEntityManagerFactory()");
        if (emf != null) {
            emf.close();
        }
    }

    @Bean
    public EntityManager getEntityManager() {
        return getEntityManagerFactory().createEntityManager();
    }
}

TestSpring应用程序

@Configuration
@ComponentScan
public class TestSpringApplication {
    @Autowired
    private ApplicationContext context;

    @Autowired
    private CrudRepository<Message, Long> messageRepository;

    final static Logger logger = LoggerFactory.getLogger(TestSpringApplication.class);

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(TestSpringApplication.class);
        TestSpringApplication app = ctx.getBean(TestSpringApplication.class);
        app.run(args);
    }

    public void run(String... args) {
        // SAVE
        logger.debug("saving message 1/one...");
        Message messageOne = messageRepository.save(new Message("Message one"));

        logger.debug("saving message 2/two...");
        Message messageTwo = messageRepository.save(new Message("Message two"));
        printAllMessages();


        // SAVE ALL
        logger.debug("saving message 2/two...");
        Message messageThree = new Message("Message three");
        Message messageFour = new Message("Message four");
        Message messageFive = new Message("Message five");
        Message messageSix = new Message("Message six");
        Message messageSeven = new Message("Message seven");
        Message messageEight = new Message("Message eight");
        List<Message> messages = Arrays.asList(messageThree, messageFour, messageFive, messageSix, messageSeven, messageEight);
        messageRepository.saveAll(messages);

        // FIND ALL
        printAllMessages();
    }

    private void printAllMessages() {
        logger.debug("finding all messages...");
        messageRepository.findAll().forEach(m -> {
            logger.debug("Message: " + m.toString());
        });
    }
}
 

使用屏幕截图成功保存数据:

enter image description here

评论

0赞 Anish B. 9/24/2023
@GaetanL。你现在可以验证了吗?
1赞 Gaetan L. 9/24/2023
非常好,谢谢你的所有努力。不能说我什么都明白,但它有效!
0赞 Anish B. 9/24/2023
@GaetanL。也请点赞。:)
0赞 Anish B. 9/24/2023
@GaetanL。请添加赏金:)。
1赞 Gaetan L. 9/25/2023
对不起,这是我第一次悬赏。上面写着“您可以在 20 小时内奖励价值 50 声望的赏金”。那你能提醒我吗?