【问题标题】:How to break a circular depencency and be canonic?如何打破循环依赖并成为规范?
【发布时间】:2020-12-21 10:36:30
【问题描述】:

我在 2 个模型之间存在循环依赖关系,但从使用的角度来看,这不是真正的依赖关系。

  • 建筑物归业主所有
  • 租户住在大楼里

根据用户的类型,与建筑物的关系有所不同。

public enum UserType
{
    TENANT,
    OWNER
}

用户:

public class User extends Model
{
    @ManyToOne
    private Building building;

    private UserType type;
}

建筑:

public class Building extends Model
{
    @ManyToOne
    private User owner;
}

它是否被认为是真正的循环依赖?我应该如何避免这种情况?

编辑:

我使用 Play Framework(使用 Guice)进行依赖注入,这也让我很头疼。代码可以编译,但没有运行。

public class BuildingRepository
{
    @Inject
    private UserRepository userRepository;

    private final Finder<Long, Building> find = new Finder<>(Building.class);

    public void save(Building building)
    {
        building.save();
    }

    public void update(Building building)
    {
        building.update();
    }

    public boolean delete(Building building)
    {
        // check if there is tenants in the building
        if (userRepository.countByBuilding(building) > 0)
        {
            return false;
        }
      
        return building.delete();
    }

    public List<Building> findFor(User user)
    {
        if (user.getType() == UserType.OWNER)
        {
            return find.query().where()
                .eq("owner.id", user.getId())
                .findList();
        }
   
        return new ArrayList<>();
    }
}

UserRepository 使用BuildingRepository,但仅适用于特定类型的用户。

public class UserRepository
{
    @Inject
    private BuildingRepository buildingRepository;

    private final Finder<Long, User> find = new Finder<>(User.class);

    public void save(User user)
    {
        user.save();
    }

    public void update(User user)
    {
        user.update();
    }

    public boolean delete(User user)
    {
        if (user.getType() == UserType.OWNER)
        {
            // remove the owner on the buildings
            for (Building building : buildingRepository.findFor(user))
            {
                if (user.equals(building.getOwner()))
                {
                    building.setOwner(null);
                    buildingRepository.update(building);
                }
            }
        }

        return user.delete();
    }

    public int countByBuilding(Building building)
    {
        return find.query().where()
            .eq("building.id", building.getId())
            .eq("status", true)
            .findCount();
    }
}

【问题讨论】:

  • 什么是“真正的”循环依赖?为什么要避免它?模块之间的循环依赖可能是有问题的,但我猜,同一个模块中的类之间这样的循环依赖是可以的。
  • 一个“真正的”循环依赖不起作用,你不能在没有另一个的情况下创建一个,反之亦然。就我而言,如果用户类型正确,它就可以工作。我想避免它,因为我的框架抱怨它。它无法编译。
  • 你把这些类放在不同的罐子里了吗?是的,然后它不会编译。你为什么把它们放在不同的罐子里?您是否打算将一个与另一个分开重复使用?
  • 不,它们在同一个包中。我计划在 Angular 中使用前端,但它也抱怨它(作为警告)。另一件事是我使用依赖注入(使用 Play 框架),它使得无法编译。
  • 啊,可能是在JSON序列化中。让我制定一个答案。

标签: java mysql jpa playframework


【解决方案1】:

警告可能来自 JSON 序列化。如果您使用这些对象直接映射到 json,则没有什么可以阻止将所有者的建筑物添加为租户类的建筑物。这是有道理的,如果业主住在他自己的大楼里。理论上你可以得到一个结构:

{
  user: {
    building: {
      owner: {
        building: {
          owner: {    
            building: {
              owner: {
                ...
}

您可以通过不递归地序列化建筑物的所有者来修复 JSON 递归。

public class Building extends Model
{
    @ManyToOne
    @JsonIgnore
    private User owner;
}

对于您实际需要所有者信息的情况,您可以创建 DTO 类以仅包含您需要的信息:

public class BuildingDTO
{
    private String ownerName;
}

编辑:

要修复存储库中的循环,您可以执行以下操作:

public class SafeDeleteService
{
    @Inject
    private UserRepository userRepository;
    @Inject
    private BuildingRepository buildingRepository;

    public boolean safeDelete(User user)
    {
        if (user.getType() == UserType.OWNER)
        {
            // remove the owner on the buildings
            for (Building building : buildingRepository.findFor(user))
            {
                if (user.equals(building.getOwner()))
                {
                    building.setOwner(null);
                    buildingRepository.update(building);
                }
            }
        }

        return userRepository.unsafeDelete(user);
    }
}

public class UserRepository
{
    public boolean unsafeDelete(User user)
    {
        return user.delete();
    }
}

【讨论】:

  • 主要问题是为了执行查询,我使用了存储库服务。服务相互注入导致循环依赖错误。我猜对租户和所有者使用单独的模型应该可以解决这个问题。
  • 服务互相注入?你能证明吗?我认为你应该有一个 BuildingRepository 和一个 UserRepository,也许还有一个 UserManagementService 和一个 HousingManagementService,但这些服务不应该相互链接。
  • 我得好好想想……谢谢!
  • 我需要在 BuildingRepository 中注入一个 UserRepository 来检查建筑物的租户。我需要在 UserRepository 中注入 BuildingRepository 来过滤结果。这是例子,但有很多情况。
  • 如果您将删除方法放在另一个类(域服务)中,该类可以同时访问 BuildingRepository 和 UserRepository,循环就会中断。然后 UserRepository 中的 delete 就只做 user.delete(),这样比较危险。
猜你喜欢
  • 2020-01-29
  • 2016-05-24
  • 2015-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多