无法从 @Transaction 中具有关系的两个表中删除
Can't delete from two tables with a relation in an @Transaction
我一直在使用 Spring 4 的 UserDetailsManager
创建用户,架构是他们的文档为 USERS
和 AUTHORITIES
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。如果这是矫枉过正或有更好的方法,请发表评论!
希望对某人有所帮助。而且是正确的!
我一直在使用 Spring 4 的 UserDetailsManager
创建用户,架构是他们的文档为 USERS
和 AUTHORITIES
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。如果这是矫枉过正或有更好的方法,请发表评论!
希望对某人有所帮助。而且是正确的!