【问题标题】:how to update an entity with spring data jpa如何使用弹簧数据 jpa 更新实体
【发布时间】:2014-03-28 16:12:41
【问题描述】:

我有一个实体和一个 Junit,我想测试更新方法是否正常工作,但是当我从 CrudRepository 调用保存方法时,我在表中得到一个新条目,而不是更新后的实体。

这是我的实体:

@Entity(name = "PERSON")
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "PERSON_ID")
    private Integer id;
    @Column(name = "FIRST_NAME")
    private String firstName;
    @Column(name = "LAST_NAME")
    private String lastName;
//getters and setters
}

这是我的服务类:

@Service
public class PersonServiceImpl implements PersonService {

    @Autowired
    private PersonRepository personRepository;

    @Override
    public Person updatePerson(Person oldPerson) throws Exception { 

        return personRepository.save(oldPerson);
    }
}

这是我的仓库

public interface PersonRepository extends CrudRepository<Person, String> {
}

这是我的测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { JPAConfigurationTest.class })
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
@Transactional
public class UpdatePersonServiceIntegrationTest {
        @Autowired
    PersonService personService;

        @Before
    public void setUp() throws Exception {
        Person person = new Person(1);
        person.setFirstName("Nicolas");
        person.setLastName("Spessot");

        personService.createPerson(person); //This invokes save
    }

        @Test
    public void updatingPerson() throws Exception{
        Person person = new Person(1);
        person.setFirstName("Juan");
        person.setLastName("Riquelme");

        personService.updatePerson(person);

        Person retrieved = personService.retrievePerson(1); //This invokes findOne

        assertEquals(1, person.getId());
        assertEquals("Juan", person.getFirstName());
        assertEquals("Riquelme", person.getLastName());
    }
}

提前致谢

【问题讨论】:

  • 确保设置了哪些值。您可以通过在 save() 调用之前记录这些值来检查。
  • 您忘记亲自实施equals()hashcode()。实现时只考虑字段id。那应该可以解决您的问题。

标签: java spring hibernate jpa


【解决方案1】:

无需为此进行任何复杂的操作。 在您的 服务类 中,您可以执行类似的操作。

 @Service
public class PersonServiceImpl implements PersonService {

    @Autowired
    private PersonRepository personRepository;

    @Override
    public Person updatePerson(Person oldPerson) throws Exception { 

      oldPerson.setId(oldPerson.getId()) ; // pass the associated id for which you want to update and set that id to the same person [ basically setting the same id to the oldPerson ] this way it will not create new entry because here we are not making new ID ] 

 //oldPerson.set others [ what is to be updated ]  


        return personRepository.save(oldPerson); // now save [old person with updated content but same id as it was before ]
    }
}

这解决了在数据库中创建新条目的问题,并使用与该人关联的 ID 更新相同的内容。

【讨论】:

    【解决方案2】:

    实现这项工作的两种方法

    将 compareTo 方法覆盖为

    @Entity(name = "PERSON")
    public class Person implements Comparable<Person>{
     //... all your attributes goes here
     private Integer id;
    
     @Override
    public int compareTo(Person person) {
       return this.getId().compareTo(person.getId());
    }} 
    

    你需要在你的实体类中重写equals和hashcode方法,如下所示

     @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (id == null || obj == null || getClass() != obj.getClass())
            return false;
        Person that = (Person) obj;
        return id.equals(that.id);
    }
    @Override
    public int hashCode() {
        return id == null ? 0 : id.hashCode();
    }
    

    【讨论】:

      【解决方案3】:

      您必须在 Person 类中实现 equals()hashCode()

      @Entity(name = "PERSON")
      public class Person {
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          @Column(name = "PERSON_ID")
          private Integer id;
          @Column(name = "FIRST_NAME")
          private String firstName;
          @Column(name = "LAST_NAME")
          private String lastName;
          //getters and setters
      
          @Override
          public boolean equals(Object obj) {
              if (this == obj)
                  return true;
              if (id == null || obj == null || getClass() != obj.getClass())
                  return false;
              Person that = (Person) obj;
              return id.equals(that.id);
          }
          @Override
          public int hashCode() {
              return id == null ? 0 : id.hashCode();
          }
      }
      

      【讨论】:

        【解决方案4】:

        问题出在服务类中的 updatePerson 方法中。具体来说:

        return personRepository.save(oldPerson);
        

        您当前所做的只是保存一个人。这就是它创建第二个条目的原因。
        你应该先找到oldPerson,

        Person p = personRepository.findOne(oldPerson.getId())
        

        然后更新它的属性,然后像以前一样保存它。 希望对您有所帮助。

        【讨论】:

        • 我认为这个答案是错误的。在 spring-data-jpa 中,保存实际上会更新对象,如本文档 5.2.1 中所述。 docs.spring.io/spring-data/jpa/docs/current/reference/html (如果是真的,那就是个坏消息了。那么如果你通过spring mvc控制器接收到一个有很多字段的实体,你不能简单地保存它,你必须设置所有字段,这意味着很多样板代码。)
        • @Adamsan 是的,它应该像这样与save 一起使用:public &lt;S extends T&gt; S save(S entity) { if (entityInformation.isNew(entity)) { em.persist(entity); return entity; } else { return em.merge(entity); } } 但它对我也不起作用。我想上面的答案有帮助
        • @Adamsan 我认为这不正确entityInformation.isNew(entity)
        【解决方案5】:

        我认为存储库应该是

        public interface PersonRepository extends CrudRepository<Person, Integer> {
        

        由于您的 Id 是整数而不是字符串,我也假设您的

        personService.createPerson(person); 
        

        内部使用repo的save方法。

        我的第二个建议是

        @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)}
        

        这意味着应用上下文也需要再次生成bean,所以请确保您在persistence.xml中的配置没有设置要创建的h2bml。 还可以考虑在您的服务中调用 flush 方法。

        【讨论】:

        • 我正在使用 @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)} 因为我正在运行多个集成测试,这会在它们之间产生冲突。我不明白为什么要在我的服务中调用flush?我猜 Spring Data 会为你做到这一点,不是吗?
        猜你喜欢
        • 2017-01-04
        • 1970-01-01
        • 2020-11-11
        • 1970-01-01
        • 1970-01-01
        • 2017-09-05
        • 2019-03-17
        • 2017-11-05
        • 1970-01-01
        相关资源
        最近更新 更多