繁体   English   中英

JPA多对多:不清楚为什么持久化后联接表为空

[英]JPA many-to-many: unclear why join table is empty after persist

我正在尝试使用JPA(Hibernate 4)+ Spring实现简单的“多对多”关联。 看过无数类似helloworld的示例,当从2个关联表中保存实体时,联接表会自动更新。

但是,就我而言,这不会发生-即使我设置了双向关联和级联, 联接表也​​不会在em.persist()上更新。 在寻找原因的同时,我在SO上得到了这个答案 ,建议使用em.persist();。 em.flush(); 解决这个问题。 我尝试过-奇迹,坚持不懈! 但为什么???

问题:

  1. 为什么要在这里使用flush()?
  2. JPA / Hibernate官方文档中是否提到了这一点?
  3. 使用多对多关联时,是否应该在每个persist()/ update()/ remove()之后调用flush()? 这种方法可能会有哪些缺点-性能,副作用?

这是相关代码。

实体类



    @Entity
    @Table(name="ROLE")
    public class Role extends EntityBase implements Comparable
    {   
       @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
       @JoinTable(name = "ROLE_PERMISSION", 
          joinColumns = @JoinColumn(name="role_id", referencedColumnName="id"),
          inverseJoinColumns = @JoinColumn(name="permission_id", referencedColumnName="id"))
       private Set permissions = new HashSet();

       //... other code (getters/setters/extra columns) is omitted ...
    }

    @Entity
    @Table(name="PERMISSION")
    public class Permission extends EntityBase
    {   
       @ManyToMany(mappedBy = "permissions", fetch = FetchType.LAZY)
       private Set roles = new HashSet();

       //... other code (getters/setters/extra columns) is omitted ...
    }

通用DAO实施(由我的具体DAO使用):



    @Repository
    @Transactional(value="transactionManager")
    public abstract class GenericDaoImpl implements GenericDao
    {  
       @PersistenceContext(unitName = "entityManagerFactory")
       protected EntityManager em;

       public T create( final T t )
       {
          em.persist(t);
          // em.flush(); - if I put this here, all works well (and fails if I'm not)
          return t;
       }

       // ... other code is omitted ...
    }

我正在尝试测试的服务层方法:



    @Component("securityService")
    public class SecurityServiceImpl implements SecurityService
    {
       // ... other code is omitted ...   

       @Transactional(value="transactionManager", 
          rollbackFor = Exception.class, readOnly = false)
       public void createRole( Role role )
       {
          Validate.notNull( role, "Role should not be null" );
          roleDao.create( role );
       }
    }

最后是我的TestNG集成测试(使用内存中的H2 DB):



    @ContextConfiguration(
       locations={"/META-INF/beans-test.xml"})
    @TransactionConfiguration(
       transactionManager = "transactionManager", defaultRollback = true)
    public class SecurityServiceImplIT 
       extends AbstractTransactionalTestNGSpringContextTests
    {
       @Autowired
       @Qualifier("securityService")
       private SecurityService securityService;

       @Test
       @Transactional(value = "transactionManager")
       public void createRole_createRoleWithPermissions()
       {
          // Add test data to DB.
          super.executeSqlScript( TESTDATA_PATH, false);
          // Remove all associations between permissions and roles, need
          // clear intermediate table for this test case.
          super.simpleJdbcTemplate.update( 
             "delete from DB_TEST.ROLE_PERMISSION;" );
          super.simpleJdbcTemplate.update( 
             "delete from DB_TEST.ROLE;" );

          final Role role = new Role();
          role.setName( "Test role" );
          role.addPermission( securityService.getAllPermissions().get(0) );
          final int expectedPermissionCount = 1;

          securityService.createRole(role);

          // This is always passed
          Assert.assertEquals( super.countRowsInTable( "DB_TEST.ROLE" ), 1, 
             "New role should be added, so table should contain 1 row" );

          // This is failed if I'm not using flush() in my DAO.
          Assert.assertEquals( super.countRowsInTable( "DB_TEST.ROLE_PERMISSION" ), 
             expectedPermissionCount, "Role-permission associations should be added" );
       }

       // ... other code is omitted ...
    }

休眠调试日志(在DAO中没有flush()调用):

Hibernate: insert into DB_TEST.ROLE (id, version, description, name) values (null, ?, ?, ?)

...

aa TRACE org.hibernate.engine.jdbc.internal.LogicalConnectionImpl: Starting after statement execution processing [ON_CLOSE]
aa TRACE org.hibernate.action.internal.UnresolvedEntityInsertActions: No unresolved entity inserts that depended on [[xxx.logic.db.model.Role#4]]
aa TRACE org.hibernate.engine.internal.Cascade: Processing cascade ACTION_PERSIST_SKIPLAZY for: xxx.logic.db.model.Role
aa TRACE org.hibernate.engine.internal.Cascade: Cascade ACTION_PERSIST_SKIPLAZY for collection: xxx.logic.db.model.Role.permissions
aa TRACE org.hibernate.engine.spi.EJB3CascadingAction: Cascading to persist: xxx.logic.db.model.Permission
aa TRACE org.hibernate.event.internal.AbstractSaveEventListener: Persistent instance of: xxx.logic.db.model.Permission
aa TRACE org.hibernate.event.internal.DefaultPersistEventListener: Ignoring persistent instance
aa TRACE org.hibernate.engine.internal.Cascade: Done cascade ACTION_PERSIST_SKIPLAZY for collection: xxx.logic.db.model.Role.permissions
aa TRACE org.hibernate.engine.internal.Cascade: Done processing cascade ACTION_PERSIST_SKIPLAZY for: xxx.logic.db.model.Role
aa TRACE org.hibernate.action.internal.UnresolvedEntityInsertActions: No entity insert actions have non-nullable, transient entity dependencies.aa TRACE org.hibernate.engine.jdbc.internal.LogicalConnectionImpl: Starting after statement execution processing [ON_CLOSE]
aa TRACE org.hibernate.action.internal.UnresolvedEntityInsertActions: No unresolved entity inserts that depended on [[xxx.logic.db.model.Role#4]]
aa TRACE org.hibernate.engine.internal.Cascade: Processing cascade ACTION_PERSIST_SKIPLAZY for: xxx.logic.db.model.Role
aa TRACE org.hibernate.engine.internal.Cascade: Cascade ACTION_PERSIST_SKIPLAZY for collection: xxx.logic.db.model.Role.permissions
aa TRACE org.hibernate.engine.spi.EJB3CascadingAction: Cascading to persist: xxx.logic.db.model.Permission
aa TRACE org.hibernate.event.internal.AbstractSaveEventListener: Persistent instance of: xxx.logic.db.model.Permission
aa TRACE org.hibernate.event.internal.DefaultPersistEventListener: Ignoring persistent instance
aa TRACE org.hibernate.engine.internal.Cascade: Done cascade ACTION_PERSIST_SKIPLAZY for collection: xxx.logic.db.model.Role.permissions
aa TRACE org.hibernate.engine.internal.Cascade: Done processing cascade ACTION_PERSIST_SKIPLAZY for: xxx.logic.db.model.Role
aa TRACE org.hibernate.action.internal.UnresolvedEntityInsertActions: No entity insert actions have non-nullable, transient entity dependencies.

该日志说Hibernate正在遍历Permissions集合,但是出于某种原因忽略了那里的项目。 我完全不明白为什么在这里调用flush()会有所不同...通常,flush()只是一种明确告诉Hibernate何时将SQL查询发布到数据库的功能。

任何人都可以解释这一点,或者至少可以向我指出正确的文档吗?

Hibernate将SQL语句的执行延迟到绝对必要为止。 这样可以避免在事务最终回滚的情况下执行不必要的语句,从而节省了时间。

最后,提交事务后,Hibernate刷新挂起的修改并提交。 但是由于您已将测试配置为回滚而不提交事务,所以这种自动刷新永远不会发生,并且您需要显式调用flush()使其执行插入。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM