2017-01-14 22 views
0

我一直在使用Spring 4的UserDetailsManager来创建用户,该架构是由他们的文档建议的USERSAUTHORITIES表。无法从@Transaction中的关系中删除两个表

我也已经使用Spring数据@Repository注释接口,其中被定义为具有在表USERSusername字段的关系的单独REGISTRATIONS表来管理数据。

我一直面临的问题是,当我想删除一个用户,我首先使用注入春天的数据仓库,随后呼叫使用UserDetailsManager删除的REGISTRATIONS表中的记录deleteUser()。 (这只是连续调用@Service带注释的类中的@Transactional方法中的两个连续调用)。

例如

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

然而,如在表REGISTRATIONS(第一线)的记录还没有被删除的用户的删除失败。随后,我得到一个异常(第二行)抱怨无法删除用户,因为REGISTRATIONS表中存在外键约束,从而防止删除它。

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

编辑:定义为

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

注册记忆表如下:

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

尝试在第一次删除后刷新EntityManager。 –

+0

注册实体是否有对用户实体的引用?或者只是某种映射到FK的ID? –

+1

补充Guillaume的建议:delete()不会删除实体。它标记要删除的实体。删除行的实际SQL查询仅在刷新时执行。 –

回答

1

所以,我的理解是,Spring的UserDetailsManager使用JDBC调用删除 '用户' 和 '机关' 从各自的表格。

我的'注册'实体由EntityManager管理,它没有与'用户'记录之间的定义关系(在ORM级别)。这种关系纯粹是在数据库层面指定的。

EntityManager将标志着“注册”实体被删除,而UserDetailsManager实际上将删除“用户”,之前已EntityManager在交易结束时被刷新恰好。由于“注册”实体尚未被删除,此操作失败,事务仍未完成,但JDBC调用已尝试删除'用户'和'权限'。

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

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,以确保我得到绑定到此事务的此线程的正确绑定。如果这是过度杀伤,或者有更好的方法来做到这一点,请评论!

希望能帮助别人。并且是正确的!

+0

其他正确的方法是在这些表之间创建一个Entity @OneToOne(或其他)链接,以便您的ORM知道该关系。当它执行脏检查时,它将检测关系并自动刷新事务。 –

+0

啊 - 我能做到吗? Spring的'UserDetailsManager'似乎没有把它返回的对象当作ORM实体。如果我能做到这一点,那么这将是我的首选方法。 – Paul

+0

您总是可以将一个Class声明为一个@Entity并在事务上下文中使用它,但对UserDetailsManager可能有点棘手(另外这不是它的目的) –