zoukankan      html  css  js  c++  java
  • Spring Data JPA教程, 第二部分: CRUD(翻译)

    我的Spring Data Jpa教程的第一部分描述了,如何配置Spring Data JPA,本博文进一步描述怎样使用Spring Data JPA创建一个简单的CRUD应用。该应用要求如下:

    • person 必须有 first name 和 last name. 这两者是强制的.
    • 能够列出所有persons.
    • 能够添加新的persons.
    • 能够编辑已存在的persons的信息.
    • 能够删除persons.

    现在我已经描述了创建的应用的要求,现在开始工作并实现它。

    所需步骤

    CRUD应用的实现可以分割成如下步骤:

    • 实现Person 模型对象
    • 为Person 对象创建repository
    • 使用创建的repository

    下面详细解释每一步骤.

    实现模型对象

    Person 类的实现是相当简单的,不过有几个问题我需要指出:

    • builder用于创建Person类的新实例. 这似乎是国度设计,不过本人喜欢这种方式,其原因有二:首先,它比telescopic constructor pattern代码更易于阅读. 其次,它确保你不能在它们的构造期间创建一个不一致状态的对象(这是通常的JavaBeans 模式 不能保证的).
    •   改变存储在Person对象里面的信息的唯一方式是调用 update()方法,我倾向尽可能的向model对象放入很多逻辑,这种方式使服务层不至于充斥领域逻辑,并且你不会以 anemic domain model结束(译者注:请参考贫血型与富血型模型).

    我的 Person 类的源码如下:

    import org.apache.commons.lang.builder.ToStringBuilder;
    
    import javax.persistence.*;
    
    /**
     * An entity class which contains the information of a single person.
     * @author Petri Kainulainen
     */
    @Entity
    @Table(name = "persons")
    public class Person {
        
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
        
        @Column(name = "creation_time", nullable = false)
        private Date creationTime;
        
        @Column(name = "first_name", nullable = false)
        private String firstName;
    
        @Column(name = "last_name", nullable = false)
        private String lastName;
        
        @Column(name = "modification_time", nullable = false)
        private Date modificationTime;
        
        @Version
        private long version = 0;
    
        public Long getId() {
            return id;
        }
    
        /**
         * Gets a builder which is used to create Person objects.
         * @param firstName The first name of the created user.
         * @param lastName  The last name of the created user.
         * @return  A new Builder instance.
         */
        public static Builder getBuilder(String firstName, String lastName) {
            return new Builder(firstName, lastName);
        }
        
        public Date getCreationTime() {
            return creationTime;
        }
    
        public String getFirstName() {
            return firstName;
        }
    
        public String getLastName() {
            return lastName;
        }
    
        /**
         * Gets the full name of the person.
         * @return  The full name of the person.
         */
        @Transient
        public String getName() {
            StringBuilder name = new StringBuilder();
            
            name.append(firstName);
            name.append(" ");
            name.append(lastName);
            
            return name.toString();
        }
    
        public Date getModificationTime() {
            return modificationTime;
        }
    
        public long getVersion() {
            return version;
        }
    
        public void update(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }
        
        @PreUpdate
        public void preUpdate() {
            modificationTime = new Date();
        }
        
        @PrePersist
        public void prePersist() {
            Date now = new Date();
            creationTime = now;
            modificationTime = now;
        }
    
        @Override
        public String toString() {
            return ToStringBuilder.reflectionToString(this);
        }
    
        /**
         * A Builder class used to create new Person objects.
         */
        public static class Builder {
            Person built;
    
            /**
             * Creates a new Builder instance.
             * @param firstName The first name of the created Person object.
             * @param lastName  The last name of the created Person object.
             */
            Builder(String firstName, String lastName) {
                built = new Person();
                built.firstName = firstName;
                built.lastName = lastName;
            }
    
            /**
             * Builds the new Person object.
             * @return  The created Person object.
             */
            public Person build() {
                return built;
            }
        }
    
        /**
         * This setter method should only be used by unit tests.
         * @param id
         */
        protected void setId(Long id) {
            this.id = id;
        }
    }

    创建Repository

    实现一个为Person模型对象提供CRUD操作的repository是相当简略的,你所要做的就是常见一个继承自JpaRepository接口的接口。 JpaRepository接口是向Repository接口的JPA规范扩展,给你访问如下方法,它们用于实现CRUD应用.

    • delete(T entity) which deletes the entity given as a parameter.
    • findAll() which returns a list of entities.
    • findOne(ID id) which returns the entity using the id given a parameter as a search criteria.
    • save(T entity) which saves the entity given as a parameter.

    我的PersonRepository 接口源码如下:

    import org.springframework.data.jpa.repository.JpaRepository;
    
    /**
     * Specifies methods used to obtain and modify person related information
     * which is stored in the database.
     * @author Petri Kainulainen
     */
    public interface PersonRepository extends JpaRepository<Person, Long> {
    }

    使用创建的Repository

    你现在已经创建model对象和与数据库交互需要的repository,下一步是实现服务类,它是控制器和实现repository之间的中介,服务层的结构下一步描述

    PersonDTO是一个简单的DTO对象,在我的示例应用中用于form对象,它的源码如下

    import org.apache.commons.lang.builder.ToStringBuilder;
    import org.hibernate.validator.constraints.NotEmpty;
    
    /**
     * A DTO object which is used as a form object
     * in create person and edit person forms.
     * @author Petri Kainulainen
     */
    public class PersonDTO {
        
        private Long id;
    
        @NotEmpty
        private String firstName;
    
        @NotEmpty
        private String lastName;
    
        public PersonDTO() {
    
        }
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getFirstName() {
            return firstName;
        }
    
        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }
    
        public String getLastName() {
            return lastName;
        }
    
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
    
        @Override
        public String toString() {
            return ToStringBuilder.reflectionToString(this);
        }
    }

    PersonService接口声明实际实现提供的方法,它的源码如下:

    /**
     * Declares methods used to obtain and modify person information.
     * @author Petri Kainulainen
     */
    public interface PersonService {
    
        /**
         * Creates a new person.
         * @param created   The information of the created person.
         * @return  The created person.
         */
        public Person create(PersonDTO created);
    
        /**
         * Deletes a person.
         * @param personId  The id of the deleted person.
         * @return  The deleted person.
         * @throws PersonNotFoundException  if no person is found with the given id.
         */
        public Person delete(Long personId) throws PersonNotFoundException;
    
        /**
         * Finds all persons.
         * @return  A list of persons.
         */
        public List<Person> findAll();
    
        /**
         * Finds person by id.
         * @param id    The id of the wanted person.
         * @return  The found person. If no person is found, this method returns null.
         */
        public Person findById(Long id);
    
        /**
         * Updates the information of a person.
         * @param updated   The information of the updated person.
         * @return  The updated person.
         * @throws PersonNotFoundException  if no person is found with given id.
         */
        public Person update(PersonDTO updated) throws PersonNotFoundException;
    }

    RepositoryPersonService类实现PersonService接口,其源码如下:

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import javax.annotation.Resource;
    
    /**
     * This implementation of the PersonService interface communicates with
     * the database by using a Spring Data JPA repository.
     * @author Petri Kainulainen
     */
    @Service
    public class RepositoryPersonService implements PersonService {
        
        private static final Logger LOGGER = LoggerFactory.getLogger(RepositoryPersonService.class);
        
        @Resource
        private PersonRepository personRepository;
    
        @Transactional
        @Override
        public Person create(PersonDTO created) {
            LOGGER.debug("Creating a new person with information: " + created);
            
            Person person = Person.getBuilder(created.getFirstName(), created.getLastName()).build();
            
            return personRepository.save(person);
        }
    
        @Transactional(rollbackFor = PersonNotFoundException.class)
        @Override
        public Person delete(Long personId) throws PersonNotFoundException {
            LOGGER.debug("Deleting person with id: " + personId);
            
            Person deleted = personRepository.findOne(personId);
            
            if (deleted == null) {
                LOGGER.debug("No person found with id: " + personId);
                throw new PersonNotFoundException();
            }
            
            personRepository.delete(deleted);
            return deleted;
        }
    
        @Transactional(readOnly = true)
        @Override
        public List<Person> findAll() {
            LOGGER.debug("Finding all persons");
            return personRepository.findAll();
        }
    
        @Transactional(readOnly = true)
        @Override
        public Person findById(Long id) {
            LOGGER.debug("Finding person by id: " + id);
            return personRepository.findOne(id);
        }
    
        @Transactional(rollbackFor = PersonNotFoundException.class)
        @Override
        public Person update(PersonDTO updated) throws PersonNotFoundException {
            LOGGER.debug("Updating person with information: " + updated);
            
            Person person = personRepository.findOne(updated.getId());
            
            if (person == null) {
                LOGGER.debug("No person found with id: " + updated.getId());
                throw new PersonNotFoundException();
            }
            
            person.update(updated.getFirstName(), updated.getLastName());
    
            return person;
        }
    
        /**
         * This setter method should be used only by unit tests.
         * @param personRepository
         */
        protected void setPersonRepository(PersonRepository personRepository) {
            this.personRepository = personRepository;
        }
    }

    本步骤的最后部分是为RepositoryPersonService类编写单元测试,这些单元测试的源码如下:

    import org.junit.Before;
    import org.junit.Test;
    import org.mockito.ArgumentCaptor;
    
    import static junit.framework.Assert.assertEquals;
    import static org.mockito.Mockito.*;
    
    public class RepositoryPersonServiceTest {
    
        private static final Long PERSON_ID = Long.valueOf(5);
        private static final String FIRST_NAME = "Foo";
        private static final String FIRST_NAME_UPDATED = "FooUpdated";
        private static final String LAST_NAME = "Bar";
        private static final String LAST_NAME_UPDATED = "BarUpdated";
        
        private RepositoryPersonService personService;
    
        private PersonRepository personRepositoryMock;
    
        @Before
        public void setUp() {
            personService = new RepositoryPersonService();
    
            personRepositoryMock = mock(PersonRepository.class);
            personService.setPersonRepository(personRepositoryMock);
        }
        
        @Test
        public void create() {
            PersonDTO created = PersonTestUtil.createDTO(null, FIRST_NAME, LAST_NAME);
            Person persisted = PersonTestUtil.createModelObject(PERSON_ID, FIRST_NAME, LAST_NAME);
            
            when(personRepositoryMock.save(any(Person.class))).thenReturn(persisted);
            
            Person returned = personService.create(created);
    
            ArgumentCaptor<Person> personArgument = ArgumentCaptor.forClass(Person.class);
            verify(personRepositoryMock, times(1)).save(personArgument.capture());
            verifyNoMoreInteractions(personRepositoryMock);
    
            assertPerson(created, personArgument.getValue());
            assertEquals(persisted, returned);
        }
        
        @Test
        public void delete() throws PersonNotFoundException {
            Person deleted = PersonTestUtil.createModelObject(PERSON_ID, FIRST_NAME, LAST_NAME);
            when(personRepositoryMock.findOne(PERSON_ID)).thenReturn(deleted);
            
            Person returned = personService.delete(PERSON_ID);
            
            verify(personRepositoryMock, times(1)).findOne(PERSON_ID);
            verify(personRepositoryMock, times(1)).delete(deleted);
            verifyNoMoreInteractions(personRepositoryMock);
            
            assertEquals(deleted, returned);
        }
        
        @Test(expected = PersonNotFoundException.class)
        public void deleteWhenPersonIsNotFound() throws PersonNotFoundException {
            when(personRepositoryMock.findOne(PERSON_ID)).thenReturn(null);
            
            personService.delete(PERSON_ID);
            
            verify(personRepositoryMock, times(1)).findOne(PERSON_ID);
            verifyNoMoreInteractions(personRepositoryMock);
        }
        
        @Test
        public void findAll() {
            List<Person> persons = new ArrayList<Person>();
            when(personRepositoryMock.findAll()).thenReturn(persons);
            
            List<Person> returned = personService.findAll();
            
            verify(personRepositoryMock, times(1)).findAll();
            verifyNoMoreInteractions(personRepositoryMock);
            
            assertEquals(persons, returned);
        }
        
        @Test
        public void findById() {
            Person person = PersonTestUtil.createModelObject(PERSON_ID, FIRST_NAME, LAST_NAME);
            when(personRepositoryMock.findOne(PERSON_ID)).thenReturn(person);
            
            Person returned = personService.findById(PERSON_ID);
            
            verify(personRepositoryMock, times(1)).findOne(PERSON_ID);
            verifyNoMoreInteractions(personRepositoryMock);
            
            assertEquals(person, returned);
        }
        
        @Test
        public void update() throws PersonNotFoundException {
            PersonDTO updated = PersonTestUtil.createDTO(PERSON_ID, FIRST_NAME_UPDATED, LAST_NAME_UPDATED);
            Person person = PersonTestUtil.createModelObject(PERSON_ID, FIRST_NAME, LAST_NAME);
            
            when(personRepositoryMock.findOne(updated.getId())).thenReturn(person);
            
            Person returned = personService.update(updated);
            
            verify(personRepositoryMock, times(1)).findOne(updated.getId());
            verifyNoMoreInteractions(personRepositoryMock);
            
            assertPerson(updated, returned);
        }
        
        @Test(expected = PersonNotFoundException.class)
        public void updateWhenPersonIsNotFound() throws PersonNotFoundException {
            PersonDTO updated = PersonTestUtil.createDTO(PERSON_ID, FIRST_NAME_UPDATED, LAST_NAME_UPDATED);
            
            when(personRepositoryMock.findOne(updated.getId())).thenReturn(null);
    
            personService.update(updated);
    
            verify(personRepositoryMock, times(1)).findOne(updated.getId());
            verifyNoMoreInteractions(personRepositoryMock);
        }
    
        private void assertPerson(PersonDTO expected, Person actual) {
            assertEquals(expected.getId(), actual.getId());
            assertEquals(expected.getFirstName(), actual.getFirstName());
            assertEquals(expected.getLastName(), expected.getLastName());
        }
    }

    下一步?

    本人已经向你演示了如何用Spring Data JPA实现一个简单的CRUD应用,如果你对查看我的全部实践的功能示例感兴趣,你可以从Github获取,我的Spring Data JPA教程的第三部分描述如何用query方法创建自定义查询

    --------------------------------------------------------------------------- 

    本系列Spring Data JPA 教程翻译系本人原创

    作者 博客园 刺猬的温驯 

    本文链接http://www.cnblogs.com/chenying99/archive/2013/06/19/3143527.html

    本文版权归作者所有,未经作者同意,严禁转载及用作商业传播,否则将追究法律责任。

  • 相关阅读:
    (第九周)视频发布及git统计报告
    (第九周)团队项目16
    (第九周)团队项目15
    (第九周)团队项目14
    C# 中请求数据方式
    C#中Json和List/DataSet相互转换
    C#枚举注释实例
    C#常用简单线程实例
    C#动态多线程实例
    MySql中文乱码
  • 原文地址:https://www.cnblogs.com/chenying99/p/3143527.html
Copyright © 2011-2022 走看看