如何使用Spring Data JPA更新实体

19

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

这是我的实体:

@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()之前,您可以通过记录这些值来检查它们。 - Dax Joshi
你忘记在Person类中实现equals()hashcode()方法了。在实现时只考虑id字段即可解决问题。 - ciri-cuervo
5个回答

28

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

return personRepository.save(oldPerson);

你目前所做的只是保存了一个人。这就是为什么它会创建第二个条目的原因。你应该先找到旧人。

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

然后更新它的属性,然后像之前一样保存它。


5
我认为这个答案是错误的。在spring-data-jpa中,保存实际上是更新对象,正如文档中的5.2.1所述。http://docs.spring.io/spring-data/jpa/docs/current/reference/html/(如果这是真的,那就不太好了。如果你通过Spring MVC控制器接收到一个有许多字段的实体,你不能简单地保存它,你必须设置所有字段,这意味着需要大量的样板代码。) - Adamsan
@Adamsan 是的,它应该像这样与 save 一起工作:`public <S extends T> S save(S entity) {if (entityInformation.isNew(entity)) { em.persist(entity); return entity; } else { return em.merge(entity); }}`但是对我来说也不起作用。我想上面的答案会有所帮助。 - kittu
@Adamsan 我认为这不正确 entityInformation.isNew(entity) - kittu

5

在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();
    }
}

1

我认为仓库应该是

public interface PersonRepository extends CrudRepository<Person, Integer> {

由于你的ID是整数而不是字符串,同时我假设你的
personService.createPerson(person); 

请在存储库内部使用save方法。

我给出的第二个建议是

@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)}

这意味着应用程序上下文和bean需要重新生成,因此请确保您在persistence.xml中的配置未设置为创建h2bml。 还要考虑在服务中调用flush方法。

我正在使用@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD),因为我正在运行多个集成测试,这会导致它们之间产生冲突。我不明白为什么应该在我的服务中调用flush?我猜Spring Data会为你做到这一点,不是吗? - nspessot

0

使其工作的两种方法

覆盖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();
}

0

这并不需要进行任何复杂的操作。 在你的服务类中可以这样做。

 @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更新相同内容的问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接