Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/387.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 需要Spring服务、存储库、实体设计模式建议_Java_Spring_Design Patterns_Entity - Fatal编程技术网

Java 需要Spring服务、存储库、实体设计模式建议

Java 需要Spring服务、存储库、实体设计模式建议,java,spring,design-patterns,entity,Java,Spring,Design Patterns,Entity,我有一个(希望是)简单的软件设计问题:我想让我的实体(=持久化到DB的域对象)保持不变。意思是:实体应该只由服务创建,并且应用程序的每个其他部分都使用只有getter方法的接口 示例: root |--- service | |--- MyService.java | |--- MyServiceImpl.java | | | |--- MyEntity.java | |--- MyEn

我有一个(希望是)简单的软件设计问题:我想让我的实体(=持久化到DB的域对象)保持不变。意思是:实体应该只由服务创建,并且应用程序的每个其他部分都使用只有getter方法的接口

示例:

root
  |--- service   
  |         |--- MyService.java
  |         |--- MyServiceImpl.java
  |         |
  |         |--- MyEntity.java
  |         |--- MyEntityImpl.java
  |         |
  |         |--- MyEntityRepository.java
  |
  |
  |------- web
            |--- MyController.java
  • MyController
    想要用
    id=5检索
    MyEntity

  • MyController
    必须询问
    MyService
    才能获取对象:
    MyService.getMyEntityById(5)

  • MyService
    将要求
    MyEntityRepository
    从数据库获取对象

  • MyService
    MyEntityInterface
    返回给
    MyController

  • 包装设计:

    root
      |--- service   
      |         |--- MyService.java
      |         |--- MyServiceImpl.java
      |         |
      |         |--- MyEntity.java
      |         |--- MyEntityImpl.java
      |         |
      |         |--- MyEntityRepository.java
      |
      |
      |------- web
                |--- MyController.java
    
    创意:

    root
      |--- service   
      |         |--- MyService.java
      |         |--- MyServiceImpl.java
      |         |
      |         |--- MyEntity.java
      |         |--- MyEntityImpl.java
      |         |
      |         |--- MyEntityRepository.java
      |
      |
      |------- web
                |--- MyController.java
    
    我的第一个想法是在
    MyEntityImpl
    中简单地使用一个包保护的构造函数,但这不适用于我正在使用的其他库(即Orika)。所以它必须是公共的

    下一个想法是使用
    MyEntity
    接口。但现在我遇到了一些问题:

    问题:

    root
      |--- service   
      |         |--- MyService.java
      |         |--- MyServiceImpl.java
      |         |
      |         |--- MyEntity.java
      |         |--- MyEntityImpl.java
      |         |
      |         |--- MyEntityRepository.java
      |
      |
      |------- web
                |--- MyController.java
    
    MyService(Impl)
    有一个名为:
    updateMyEntityData(myEntitye,Data Data)
    的方法。现在我无法确定在我的服务中,这个
    MyEntity
    对象是否真的是
    MyEntityImpl
    的实例。当然我可以做
    如果(MyEntityImpl的实例)…
    但这正是我不想做的

    下一个问题是:此服务方法使用
    MyEntityRepository
    ,它可以保存和检索
    MyEntityImpl
    对象,但不能处理
    MyEntity
    接口。作为一种解决方法,我可以进行额外的DB查询,但这不是我想要的:

    void updateMyEntityData(MyEntity e, Data data) {
      MyEntityImpl impl = repo.findOne(e.getId());
      impl.setData(data);
      repo.saveToDB(impl);
    }
    
    这是一个不必要的DB查询,因为我知道
    MyEntity
    MyEntityImpl
    的一个实例,并且它是由该服务创建的,因此它必须是DB中的一个对象。另一种可能是使用铸件:

    void updateMyEntityData(MyEntity e, Data data) {
      MyEntityImpl impl = (MyEntityImpl) e;
      impl.setData(data);
      repo.saveToDB(impl);
    }
    
    摘要:

    root
      |--- service   
      |         |--- MyService.java
      |         |--- MyServiceImpl.java
      |         |
      |         |--- MyEntity.java
      |         |--- MyEntityImpl.java
      |         |
      |         |--- MyEntityRepository.java
      |
      |
      |------- web
                |--- MyController.java
    
    • 仅允许服务构造
      MyEntityImpl
    • MyService(Impl)
      之后必须能够修改
      MyEntityImpl
      的字段(意味着:必须有setter)
    • 避免不必要的数据库查询

    提前谢谢你

    我认为你需要克服公共构造函数。如果只有从repository/DB检索到的对象才能分配有效标识,则可以使用该标识控制更新

    是的,你可以猜测身份,但是你可以做大量愚蠢的事情来解决你认为你正在实施的任何保护——当然,如果我选择的话,我可以创建一个实例并分配带有重新影响的字段

    现在,不变性是一个更崇高的目标,至少在多线程环境中是如此(如果您不是在多线程执行更新的环境中,那么好处就不那么明显,而且,依我看,不值得付出代价)

    问题是不可变性与通常会发生变异的域实体发生冲突。解决这个问题的一种常见方法是包含一个时间戳,指示最后一次突变发生的时间,并使用突变拷贝。下面是一个使用生成器模式创建变异副本的干净方法示例:

    调用代码如下所示:

    MyEntity mutatedMyEntity = myEntity.copy().setData(new Data()).build();
    
    root
      |--- service   
      |         |--- MyService.java (public interface)
      |         |--- MyServiceImpl.java (package protected class implements MyService)
      |         |
      |         |--- MyEntity.java (public class)
      |         |
      |         |--- MyEntityRepository.java (package protected)
      |
      |
      |------- web
                |--- MyController.java
    
    虽然这种方法使事情相对干净,但它引入了多个线程同时创建多个变异副本的问题


    根据您的具体需求,这意味着您需要在提交更改时检测冲突(您的
    saveToDB
    方法),方法是检查更改后的时间戳是否为最新版本(为了避免两次数据库命中,最好在存储过程中进行大量操作,尽管替代方法是在执行写操作的类中保留一个身份缓存,以替换经过修改的时间戳)。冲突的解决将再次取决于您的具体要求,以及对同一实体的其他实例进行的更改。

    我认为您需要放弃公共构造函数。鉴于只有从存储库/DB检索的对象才能分配有效标识,您可以使用它来控制更新

    是的,你可以猜测身份,但是你可以做大量愚蠢的事情来解决你认为你正在实施的任何保护——当然,如果我选择的话,我可以创建一个实例并分配带有重新影响的字段

    现在,不变性是一个更崇高的目标,至少在多线程环境中是如此(如果您不是在多线程执行更新的环境中,那么好处就不那么明显,而且,依我看,不值得付出代价)

    问题是不可变性与通常会发生变异的域实体冲突。解决此问题的常见方法是包含一个时间戳,指示上一次变异的提交时间,并使用变异副本。以下是使用生成器模式创建变异副本的干净方法示例:

    调用代码如下所示:

    MyEntity mutatedMyEntity = myEntity.copy().setData(new Data()).build();
    
    root
      |--- service   
      |         |--- MyService.java (public interface)
      |         |--- MyServiceImpl.java (package protected class implements MyService)
      |         |
      |         |--- MyEntity.java (public class)
      |         |
      |         |--- MyEntityRepository.java (package protected)
      |
      |
      |------- web
                |--- MyController.java
    
    虽然这种方法使事情相对干净,但它引入了多个线程同时创建多个变异副本的问题

    根据您的具体需求,这意味着您需要在提交更改时检测冲突(您的
    saveToDB
    方法),方法是检查更改后的时间戳是否为最新版本(为了避免两次数据库命中,最好在存储过程中进行大量操作,尽管替代方法是在执行写操作的类中保留一个身份缓存,以替换经过修改的时间戳)。冲突的解决将再次取决于您的具体需求,正如建议的那样