无法从 @Transaction 中具有关系的两个表中删除

Can't delete from two tables with a relation in an @Transaction

我一直在使用 Spring 4 的 UserDetailsManager 创建用户,架构是他们的文档为 USERSAUTHORITIES table 建议的架构s.

我也一直在使用 Spring 数据 @Repository 注释接口来管理单独的 REGISTRATIONS table 中的数据,它被定义为与 username 字段在 USERS table.

我一直面临的问题是,当我想删除用户时,我首先使用注入的 Spring 数据存储库从 REGISTRATIONS table 中删除记录,然后使用 UserDetailsManager 调用 deleteUser()。 (这只是在 @Service 注释 class 中的 @Transactional 方法中的两次连续调用。

例如

registrationsRepository.delete(uuid);
userDetailsManager.deleteUser(registration.getUsername());

但是,由于REGISTRATIONS table(第一行)中的记录还没有被删除,所以删除用户失败了。随后我得到一个异常(第 2 行)抱怨无法删除用户,因为 REGISTRATIONS table 中有外键约束阻止它被删除。

如果这些更新发生在同一个事务中,为什么会失败?

编辑:

@Repository
public interface RegistrationsRepository extends CrudRepository<Registration, UUID>
{
    // No EntityManager injected - uses Spring Data method queries
    // No additional methods defined
}

注册table定义如下:

CREATE TABLE Registrations (
  username varchar(64) NOT NULL REFERENCES Users (username),
  uuid UUID NOT NULL PRIMARY KEY
);

因此,据我了解,Spring 的 UserDetailsManager 使用 JDBC 调用从各自的表中删除 'users' 和 'authorities'。

我的 'registrations' 实体由 EntityManager 管理,该 EntityManager 与 'user' 记录没有明确的关系(在 ORM 级别)。这种关系纯粹是在数据库级别指定的。

EntityManager 会标记要删除的 'registration' 实体,而 UserDetailsManager 实际上会删除 'user',这发生在 EntityManager 之前已在事务结束时被刷新。这失败了,因为 'registration' 实体尚未被删除,交易仍未完成,但 JDBC 调用已经尝试删除 'user' 和 'authorities' .

为了解决这个问题,我做了以下操作。

class DefaultService implements MyService {

    private final EntityManagerFactory emf;

    // Inject RegistrationRepository and UserDetailsManager...

    @Inject
    public DefaultService(EntityManagerFactory emf, ...) {
        // ...
        this.emf = emf;
    }

    @Override
    @Transactional
    public void serviceMethod(UUID uuid, String username) {
        registrationsRepository.delete(uuid);

        // Flush the entity manager to remove this record from the DB first.
        EntityManagerHolder entityManagerHolder = (EntityManagerHolder) TransactionSynchronizationManager.getResource(emf);
        entityManagerHolder.getEntityManager().flush();

        // These will be JDBC calls, 'users' are not managed entities
        userDetailsManager.deleteUser(username);
    }
}

我以这种方式获得了 EntityManager,以确保我为该事务获得绑定到该线程的正确 EntityManager。如果这是矫枉过正或有更好的方法,请发表评论!

希望对某人有所帮助。而且是正确的!