【问题标题】:Spring data : CrudRepository's save method and updateSpring data : CrudRepository 的保存方法和更新
【发布时间】:2016-12-18 01:02:26
【问题描述】:

我想知道CrudRepository 中的{save} 方法是否在数据库中找到类似的条目时进行更新:

@Repository
public interface ProjectDAO extends CrudRepository<Project, Integer> {}

@Service
public class ProjectServiceImpl {

@Autowired private ProjectDAO pDAO;

public void save(Project p) { pDAO.save(p); } }

所以如果我在一个已注册的条目上调用该方法,它会在发现更改的属性时更新它?

谢谢。

【问题讨论】:

    标签: database hibernate spring-mvc spring-data spring-data-jpa


    【解决方案1】:

    我想知道 CrudRepository 中的 {save} 方法是否进行了更新 如果它已经在数据库中找到该条目

    有关它的 Spring 文档并不精确:

    保存给定的实体。使用返回的实例进行进一步操作 因为保存操作可能已经改变了实体实例 完全。

    但是由于CrudRepository 接口没有提出另一种具有显式命名来更新实体的方法,我们可以假设是的,因为预期 CRUD 会执行所有 CRUD 操作(CREATE、READ、UPDATE、DELETE)。

    SimpleJpaRepository 的实现证实了这一假设 类是CrudRepository 的默认实现,表明这两种情况都由方法处理:

    @Transactional
    public <S extends T> S save(S entity) {
    
        if (entityInformation.isNew(entity)) {
            em.persist(entity);
            return entity;
        } else {
            return em.merge(entity);
        }
    }
    

    因此,如果我在已注册的条目上调用该方法,它将更新 如果它发现一个改变的属性?

    在这种情况下它会做一个合并操作。所以所有字段都会根据合并级联和只读选项的设置方式进行更新。

    【讨论】:

    • 所以如果实体有一个复杂的类型,比如说一个图像,我想我需要徒手更新。
    • 图片是什么意思?如果它不是 jpa 关系字段,它确实会在任何情况下更新。因此,如果要保存的实体中的字段为 null,它将在现有行中将其值覆盖为 null。
    • 知道了。另一件事,在这里我看到'@Transactionel',所以我不需要将该注释添加到服务类中没有?
    • @Transactional 默认使用传播:Propagation.REQUIRED,它支持当前事务,如果不存在则创建一个新事务。所以,是的,没有必要在您的服务中使用@Transactional,但如果您想从服务方法手动处理回滚。如果我解决了您的问题,请毫不犹豫地接受它。
    • 我正在尝试在 Spring boot 2.5.2 上使用它但它不起作用,它忽略实体的 ID 并分配一个新的,从而创建一个全新的实体(行) .
    【解决方案2】:

    看CrudRepository接口的默认实现

     /*
         * (non-Javadoc)
         * @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
         */
        @Transactional
        public <S extends T> S save(S entity) {
    
            if (entityInformation.isNew(entity)) {
                em.persist(entity);
                return entity;
            } else {
                return em.merge(entity);
            }
        }
    

    保存方法管理两种情况:

    -如果人员 ID 为空(创建了一个新实体),则保存将调用持久方法 => 将执行插入查询。

    -如果人员 id 不为 null,则 save 将调用合并:从 entityManagerFactory 获取现有实体(如果不存在则从 2 级缓存中获取,则将从数据库中获取)并将分离的实体与托管并最终通过调用更新查询将更改传播到数据库。

    【讨论】:

      【解决方案3】:

      确切地说,如果 id 为空,save(obj) 方法会将 obj 视为 new 记录(因此会进行插入),并将处理 obj如果 id 已填写,则作为 现有 记录(因此将进行合并)。

      为什么这很重要?

      假设 Project 对象包含一个自动生成的 id 和一个必须唯一的 person_id。您创建一个 Project 对象并填写 person_id 但不填写 id 然后尝试保存。 Hibernate 会尝试插入这条记录,因为 id 是空的,但是如果那个人已经存在于数据库中,你会得到一个重复键异常。

      如何处理
      要么执行 findByPersonId(id) 来检查 obj 是否已经在数据库中,如果找到,则从中获取 id,
      或者只是尝试保存并捕获异常,在这种情况下您知道它已经在数据库中并且您需要在保存之前获取并设置 id。

      【讨论】:

        【解决方案4】:

        我想知道如果 CrudRepository 中的 {save} 方法已经在数据库中找到该条目,它是否会进行更新:

        答案是肯定的,如果找到条目就会更新:

        来自 Spring 文档:Herehttps://docs.spring.io/spring-data/jpa/docs/1.5.0.RELEASE/reference/html/jpa.repositories.html?

        可以通过 CrudRepository.save(...) 方法保存实体。它将使用底层 JPA EntityManager 持久化或合并给定的实体。如果实体尚未持久化,Spring Data JPA 将通过调用 entityManager.persist(...)-Method 来保存实体,否则将调用 entityManager.merge(...)-Method。

        【讨论】:

          【解决方案5】:

          在我的情况下,我必须将 id 属性添加到实体,并像这样放置注释 @Id。

          @Id
          private String id;
          

          这样当你获取到对象的时候,就有了数据库中实体的Id,并且执行的是Update操作而不是Create。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-12-16
            • 1970-01-01
            • 1970-01-01
            • 2016-01-29
            • 1970-01-01
            • 1970-01-01
            • 2015-06-19
            • 1970-01-01
            相关资源
            最近更新 更多