当我不想更新实体时,Spring Data JPA 会更新它

Posted

技术标签:

【中文标题】当我不想更新实体时,Spring Data JPA 会更新它【英文标题】:Spring Data JPA updates entity when I don't want it to 【发布时间】:2019-04-04 00:38:24 【问题描述】:

我有一种情况,我想检索数据,但在返回之前我想在不保存/持久化的情况下更改它。

这是我的休息控制器:

@RestController
@RequestMapping("api/drawing-releases")
@CrossOrigin("*")
public class DrawingReleaseRestController 
    @Autowired
    private DrawingReleaseService service;

    @RequestMapping(method = RequestMethod.GET, value = "/id")
    @JsonView(View.DrawingReleaseView.class)
    public ResponseEntity<DrawingRelease> getWithId(@PathVariable int id) throws EntityNotFoundException 
      return new ResponseEntity<>(service.getByID(id),HttpStatus.OK);
    

这里是服务实现:

@Service
public class DrawingReleaseServiceImpl extends DrawingFunctions implements DrawingReleaseService 
    /**
     * Logging Manager
     */
    private static final Logger LOGGER=LogManager.getLogger();

    @Autowired
    private DrawingReleaseRepository repository;

    @Override
    public DrawingRelease getByID(int id) throws EntityNotFoundException 
        Optional<DrawingRelease> opt = repository.findById(id);
        ...
        // manipulate the data here
        ...
        return opt.get();
    

最初我在服务上有@Transactional 注释。通过删除它而不在 getByID 方法上使用它,我首先认为会话将​​与存储库一起打开和关闭,如答案 here 所示。

那不起作用,我在 cmets 中看到“会话持续整个 HTTP 请求处理期间”。所以,在我添加的服务中

@Autowired
private EntityManager em;

然后在我添加的getByID方法中

em.close();

在我进行任何更改之前。但是,我所做的任何更改仍在保留中。

有什么方法可以在我的服务层中进行未保存的更改?我想我可以创建一些 POJO 来镜像实体(但不是实体),然后复制数据并返回这些对象,但似乎我不应该做这样的事情。

【问题讨论】:

【参考方案1】:

您说您想要获取一个实体,然后更改数据,然后在没有持久化实体的情况下返回它,但恕我直言,您不应该为此使用实体。为什么要更改数据以及用户将如何处理它,因为它不再代表数据库中的内容。还将进行哪些其他更改或哪些其他假设(这是一个数据库对象)将无效?

如果您使用数据库对象来派生不同的对象,您应该为此目的使用不同的类。这很常见,称为数据传输对象(DTO),它们可以直接使用投影从 spring-data-jpa 创建。您的 Dto 对象会将实体作为构造函数并填写它想要从实体中获取的任何属性。 Dto 不是持久对象,因此保存它没有问题。

更加清晰易懂的设计,与SOLID Principles and code structure一致。

【讨论】:

也许我应该进一步研究一下。至于我为什么要这样做——我有一张可以有零件清单的图纸。任何图纸都可用于另一张图纸的零件清单。就其本身而言,一张图可以有部件 1、2 和 3。如果该图是另一个部件列表的一部分,比如在第 12 行,那么我希望它显示为 12、12-1、12-2 和 12 -3。在另一幅图中,它可能在第 15 行的零件清单上,因此是 15-1、15-2 等。我仍然希望将它作为项目 1、2 和 3 保存在数据库中,但显示为其他内容视情况而定。 正确,完全正确。将您的问题域与持久性域分开。您不想修改零件中的信息,因为绘图是如何使用它的,特别是因为每张绘图都会以不同的方式使用它。查看一些围绕这个概念的设计模式。我不想逃跑并开始建议,但绝对不要因此而改变实体。【参考方案2】:

是的,你可以detach来自持久化上下文的实体:https://docs.oracle.com/javaee/7/api/javax/persistence/EntityManager.html#detach-java.lang.Object-

【讨论】:

detached ***对象,但它仍然保留较低级别的对象。如果我detach 是较低级别的对象,那么我会得到一个 PersistentObjectException: detached entity pass to persist。有什么想法吗? 如果我调用任何存储库,即使存储库与分离实体无关,我似乎也会收到分离实体异常。这是意料之中的吗,有没有办法解决这个问题? spring-data-jpa 为每个存储库使用单独的实体管理器,它自己管理连接,因此尝试使用 spring-data-jpa 使 JPA 变得复杂将是一个挫折的练习。您应该决定是要使用 JPA 还是 spring-data-jpa,并将您的想法集中在其中一个上。 @K.Nicholas 我并没有真正关注你。专注于其中之一的方法是什么? ***和低级我假设你是指父母和孩子?还是继承?由于您假设我们知道您的代码,因此不清楚您的问题是什么。简而言之,spring-data-jpa 不是 jpa。它是 jpa 之上的一个抽象层。我是说在尝试错误地使用 spring-data-jpa 之前,您需要先了解这一点。其次,我认为更重要的是,请参阅下面的答案。

以上是关于当我不想更新实体时,Spring Data JPA 会更新它的主要内容,如果未能解决你的问题,请参考以下文章

使用 spring data jpa 更新单个字段

Spring Data Jpa:保存方法仅返回选择,但不执行插入

即使 JPA 实体不脏,我是不是可以强制 spring-data 更新可审计字段?

如何在 Spring Data 中漂亮地更新 JPA 实体?

Spring Data JPA 中使用Update Query更新实体类问题

Spring data jpa 插入多个表以避免锁定表