当前位置: 首页 > 知识库问答 >
问题:

如何防止使用guice persist和@Transactional重用EntityManager?

傅穆冉
2023-03-14

根据这个问题,当使用guice persist时,EntityManager是事务范围。如果我理解正确,这意味着将为每个事务创建一个新的EntityManager。使用guice persist时,建议使用提供所有绑定的JpaPersistModule,只需插入提供者即可

public class ProjectDAO {

  private final Provider<EntityManager> entityManagerProvider;

  @Inject
  public ProjectDAO(Provider<EntityManager> entityManagerProvider) {

    this.entityManagerProvider = entityManagerProvider;
  }
} 

注意:在这个答案中,它表示不应直接注入EntityManager,而应使用Provider

现在,我已经试过了,每个线程都有自己的EntityManager。但是,它似乎没有被删除并重新设置,而是被重新用于后续事务,即Hibernate的一级缓存没有被清除。

下面是一个完整的示例,它从两个不同的线程(顺序地,而不是并行地)插入和删除一些实体,这会导致一个线程具有过时的信息:

项目(一个简单的实体)

    @NamedQueries({
        @NamedQuery(name = "project.findAll", query = "from project"),
        @NamedQuery(name = "project.deleteByProjectName", query = "delete from project p where p.name = :project_name")
    }
    )
    @Entity(name = "project")
    public class Project {

        @Id
        @GeneratedValue
        private Long id;

        @Column(name="name")
        private String name;

        // ... getters/setters
    }

项目道

    public class ProjectDAO {

      private final Provider<EntityManager> entityManagerProvider;

      @Inject
      public ProjectDAO(Provider<EntityManager> entityManagerProvider) {
        this.entityManagerProvider = entityManagerProvider;
      }

      public void insert(Project project) {
        entityManagerProvider.get().persist(project);
      }

      public List<Project> findAll() {

        return entityManagerProvider.get()
            .createNamedQuery("project.findAll", Project.class)
            .getResultList();
      }

      public void delete(String projectName) {

        entityManagerProvider.get()
            .createNamedQuery("project.deleteByProjectName")
            .setParameter("project_name", projectName)
            .executeUpdate(); 
      }

      public Project findById(Long id) {

        return entityManagerProvider.get().find(Project.class, id);
      }
    }

项目服务

    public class ProjectService {

      private final ProjectDAO projectDAO;

      @Inject
      public ProjectService(ProjectDAO projectDAO) {

        this.projectDAO = projectDAO;
      }

      @Transactional
      public void addProject(Project project) {
        projectDAO.insert(project);
      }

      @Transactional
      public List<Project> findAll() {
        return projectDAO.findAll();
      }

      @Transactional
      public void delete(String projectName) {
        projectDAO.delete(projectName);
      }

      @Transactional
      public Project findById(Long id) {
        return projectDAO.findById(id);
      }

      public EntityManager getEntityManager() {
        return projectDAO.getEntityManager();
      }
    }

主课

    public class Start {

      public static void main(String[] args) throws InterruptedException {

        Injector injector = Guice.createInjector(new AbstractModule() {
          @Override 
          protected void configure() {
            install(new JpaPersistModule("hibernatetesting"));
            bind(ProjectService.class).in(Scopes.SINGLETON);
          }
        });

        ProjectService projectService = injector.getInstance(ProjectService.class);
        PersistService persistService = injector.getInstance(PersistService.class);

        persistService.start();

        // For the purpose of making transactions from different threads, we
        // create two single threaded executors
        ExecutorService executorService1 = Executors.newSingleThreadExecutor();

        ExecutorService executorService2 = Executors.newSingleThreadExecutor();

        // Execute a few queries from Thread 1
        CountDownLatch countDownLatch1 = new CountDownLatch(1);

        executorService1.execute(() -> {
        System.out.println("TEST: " + Thread.currentThread().getName());
        System.out.println("TEST: " +"EntityManager: " + projectService.getEntityManager().hashCode());
          projectService.addProject(new Project("project1"));
          projectService.addProject(new Project("project2"));
          countDownLatch1.countDown();
        });

        countDownLatch1.await();


        // Execute a few queries from Thread 2
        CountDownLatch countDownLatch2 = new CountDownLatch(1);

        executorService2.execute(() -> {
          System.out.println("TEST: " + Thread.currentThread().getName());
          System.out.println("TEST: " +"EntityManager: " + projectService.getEntityManager().hashCode());
          projectService.addProject(new Project("project3"));
          projectService.addProject(new Project("project4"));

          //----
          projectService.delete("project1");
          //----

          // project3 is not shown in this list
          projectService.findAll().forEach(System.out::println);
          countDownLatch2.countDown();
        });

        countDownLatch2.await();

        // Execute a few more queries from Thread 1
        CountDownLatch countDownLatch3 = new CountDownLatch(1);

        executorService1.execute(() -> {
          System.out.println("TEST: " + Thread.currentThread().getName());
          System.out.println("TEST: " +"EntityManager: " + projectService.getEntityManager().hashCode());
          projectService.addProject(new Project("project5"));
          projectService.addProject(new Project("project6"));

          // project3, which was deleted in Thread 2 is still visible in
          // this EntityManager
          // ----
          Project project = projectService.findById(3L);
          System.out.println("Project still exists " + project);
          // ----

          projectService.findAll().forEach(System.out::println);
          countDownLatch3.countDown();
        });

        countDownLatch3.await();

      }
    }

pom.xml

    ...
    <dependencies>

      <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>4.3.11.Final</version>
      </dependency>

      <dependency>
        <groupId>com.google.inject</groupId>
        <artifactId>guice</artifactId>
        <version>4.2.2</version>
      </dependency>

      <dependency>
        <groupId>com.google.inject.extensions</groupId>
        <artifactId>guice-persist</artifactId>
        <version>4.2.2</version>
      </dependency>

      <dependency>
        <groupId>org.hsqldb</groupId>
        <artifactId>hsqldb</artifactId>
        <version>2.5.0</version>
      </dependency>

      <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-entitymanager</artifactId>
        <version>4.3.11.Final</version>
      </dependency>

    </dependencies>
    ...

坚持不懈xml

    <?xml version="1.0" encoding="UTF-8"?>
    <persistence 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_1_0.xsd" version="1.0">

    <persistence-unit name="hibernatetesting" transaction-type="RESOURCE_LOCAL">
      <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>

      <properties>
        <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/>
        <property name="hibernate.connection.url" value="jdbc:hsqldb:mem:testDB"/>

        <property name="hibernate.show_sql" value="true" />
        <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
        <property name="hibernate.hbm2ddl.auto" value="create" />

      </properties>
    </persistence-unit>
    </persistence>

1) 这是将EntityManager与guice persist一起使用并解决不同线程可能具有不同状态这一事实的常用方法吗?

2) 如果没有,如何确保在每次事务后在ThreadLocal上重新设置EntityManager?


共有1个答案

巫煌
2023-03-14

上述代码中有两个问题:

1) 下面一行

System.out.println("TEST: " +"EntityManager: " + projectService.getEntityManager().hashCode());

是为了调试而添加的。然而,方法ProjectService。getEntityManager(),它调用ProjectDAO。getEntityManager()。get(),未使用@Transactional注释。这会导致每个线程设置一次EntityManager,并且永远不会取消设置,即使稍后会调用ProjectService中具有@Transactional注释的其他方法。只需添加此注释即可解决问题。

2) 在一个线程中,名为“project1”的实体被删除

   //----
   projectService.delete("project1");
   //----

然而,在另一个线程中,验证另一个实体的存在

   // project3, which was deleted in Thread 2 is still visible in this EntityManager
   Project project = projectService.findById(3L);
   System.out.println("Project still exists " + project);

它从一开始就没有被删除过。一个接一个地添加实体-project1、project2、project3。。。他们被分配了ID 1,2,3。。。分别地所以代码应该是

java prettyprint-override">   // project1, which was deleted in Thread 2 is still visible in this EntityManager
   Project project = projectService.findById(1L);
   System.out.println("Project still exists " + project);
 类似资料:
  • 这是我的用户注册数据库的方式,但我的问题是:如何防止数据库具有相同用户名的副本,或者换句话说,如果用户名存在于数据库。

  • 本文向大家介绍如何防止cookie被盗用?相关面试题,主要包含被问及如何防止cookie被盗用?时的应答技巧和注意事项,需要的朋友参考一下 禁止第三方网站带cookie(same-site属性) 每次请求需要输入图形验证码 使用Token验证 为cookie设置HttpOnly 设置CSP 使用Referer验证 禁止网页内嵌 使用https cookie带上用户ip加密

  • 我目前在PHP上使用Imagick库,并使用Image Magick的调整大小功能。我刚刚了解了减压炸弹以及ImageMagick是如何容易受到它的攻击的。 我已经检查了如何ping图像并验证图像的尺寸,而不实际将其加载到内存/磁盘中。限制ImageMagick的内存和磁盘限制也更安全,这样它就不会只在磁盘上写一个大文件。 我已经读过了,我可以用setResourceLimit()实现这一点。ht

  • 我有一行代码: 它工作良好,但用户可以简单地按下Esc键来取消操作。我试图使用一个事件侦听器来防止Esc键被按下,但它只在用户在初始页面时起作用。一旦他们被重定向,它就会停止工作,他们可以在浏览器中快速按Esc键或地址栏旁边的大X来取消重定向。 有什么办法可以让我完全避免? 编辑:我之所以要这样做,是因为在登录时,它们会自动发送到索引页。我在我的数据库中有一个标志,它检查用户是否已经付款。然后在索

  • 我想防止重复的值进入数据库表从一个使用PHP的表单。 具有名为clients的表的数据库: 一个名为form的简单表单。html 名为frm_脚本的表单处理脚本。php 到目前为止,我的frm_脚本。php文件,上面的作品和一个独特的记录显示客户添加。然而,对于重复记录,它抛出“错误查询数据库”。 如何更新以下frm_script.php脚本? 如果在输入名/姓组合时发现重复行,则应显示消息“Cl

  • 问题内容: 如果我创建两个列表并将其压缩 然后我将z输入到两个列表中 那么,l1的内容就很好了[(1,7),(2,8),(3,9)],但是l2的内容只是[]。 我猜想这是python关于可迭代项的一般行为。但是,作为从C家族迁移的新手程序员,这对我来说没有意义。为什么会这样表现?有办法解决这个问题吗? 我的意思是,是的,在这个特定示例中,我可以将l1复制到l2中,但是总的来说,在迭代一次之后,是否