hibernate 如何在JPA中从父关系中正确删除ManyToOne关系?

dffbzjpn  于 2022-11-14  发布在  其他
关注(0)|答案(3)|浏览(161)

我在试着理解如何恰当地删除多对一关系。
假设我有以下实体:

@Entity
@Table(name = "user")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    
    private String name;
    
    private String lastname;
    
}
@Entity
@Table(name = "badge")
public class Badge {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    
    private String code;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "badge_id")
    private User user;
    
}

现在,这两个实体拥有不同的控制器和服务。我可以删除BadgeService中的徽章,也可以删除其服务中的用户。

@Service
public class BadgeService {

    @Autowired
    BadgeRepository badgeRepository;
    
    public void delete(int id) {
        badgeRepository.deleteById(id);
    }
    
}
@Service
public class UserService {

    @Autowired
    UserRepository userRepository;
    
    public void delete(int id) {
        userRepository.deleteById(id);
    }
    
}

问题是,如果我删除一个徽章,一切正常,但如果我删除一个用户,得到一个错误,由于FK。
为了解决这个问题,我想出了两种方法,但我想知道是否有更好的方法来处理这种问题:

第一条路

我只需在徽章存储库中创建一个方法来删除与特定用户相关的所有徽章。

public interface BadgeRepository extends CrudRepository<Badge, Integer> {

    @Modifying
    @Query(value = "DELETE Badge b WHERE b.user.id = :userId")
    public void deleteByUserId(@Param("userId") int userId);

}

然后,我在徽章服务中创建了一个方法。

@Service
public class BadgeService {

    @Autowired
    BadgeRepository badgeRepository;

    public void delete(int id) {
        badgeRepository.deleteById(id);
    }

    @Transactional
    public void deleteByUserId(int userId) {
        badgeRepository.deleteByUserId(userId);
    }

}

最后,我简单地在用户服务中自动绑定徽章服务,并在用户删除中调用该方法。

@Service
public class UserService {

    @Autowired
    UserRepository userRepository;
    
    @Autowired
    BadgeService badgeService;
    
    @Transactional
    public void delete(int id) {
        badgeService.deleteByUserId(id);
        userRepository.deleteById(id);
    }
    
}

缺点:

如果我与用户实体有多个关系,我将最终在用户服务中自动连接许多服务,这是不好的。

第二条路

我在User和Badge之间创建了双向关系,而不是单向关系。

@Entity
@Table(name = "user")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String name;

    private String lastname;

    @OneToMany(mappedBy = "user", cascade = CascadeType.REMOVE, orphanRemoval = true)
    private List<Badge> badges = new ArrayList<Badge>();

}

当我删除一个用户时,层叠或简单地从团队中删除徽章将删除所有相关的徽章。

缺点:

1.额外查询
1.如果采集量太大,APP性能会下降
话虽如此,你会有什么建议呢?第一种方法还是第二种方法?也许有更好的方法来处理这个问题?
谢谢大家。

qyzbxkaa

qyzbxkaa1#

通过处理用户服务中的徽章删除,您可以清楚地了解在删除用户时应该发生什么。现在,任何删除用户的人都将删除其关系(您可以将其视为副作用)。所以第一种方法是你能做的最好的。
只要确保不要尝试从徽章服务中删除用户即可。这将意味着循环依赖,并会造成关于谁是关系所有者的混乱。

unftdfkk

unftdfkk2#

在这种情况下,第一种方法看起来很好。要保持UserService干净,您可以在User Entity上创建PreRemove lisener,它将在删除用户之前删除所有关联,这将有助于保持UserService干净。这里要处理的下一件事是,您需要更改Casade

@Entity
@Table(name = "user")
@EntityListener(UderDeletionListener.class)
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String name;

    private String lastname;

    @OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
    private List<Badge> badges = new ArrayList<Badge>();

}

@Entity
@Table(name = "badge")
public class Badge {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    
    private String code;
    
    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
    @JoinColumn(name = "badge_id")
    private User user;
    
}

@Component
@RequiredArgsConstructor
public class UderDeletionListener {
  private final BadgeRepository badgeRepo;

  @PreRemove
  @Transactional(propagation = Propagation.REQUIRES_NEW)
  public void onDeletion(final User user) {
   badgeRepo.deleteByUserId(user.getId());
  }
}
6psbrbz9

6psbrbz93#

在这种情况下,最好将你的徽章存档,而不是直接删除。

@Entity
@Table(name = "badge")
public class Badge {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    
    private String code;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "badge_id")
    private User user;

    @Column(name = "archived")
    private boolean archived;

}

因为用户实体也可以与一些其他实体相关联。
因此,您可以将批处理实体标记为已存档,而不是删除。
或者您可以尝试以下操作...首先更新批处理用户的关系,将其设置为空,然后删除徽章。

@Service
public class BadgeService {

    @Autowired
    BadgeRepository badgeRepository;
    
    public void updateBadgeUser(int id) {
        Badge badge = findBadgeById(id);
        badge.setUser(null);
    }

    public void deleteBadge(int id) {
        
       badgeRepository.deleteById(id);
    }
    
}

但我建议你遵循档案政策。

相关问题