如何在 Spring 引导中插入和 return 完整对象

How to insert and return the full object in Spring Boot

这个问题类似于this one,但我的问题是Spring Boot 的原生CrudRepository 和JpaRepository,它们似乎没有refresh() 方法。我正在保存一个包含现有子对象 ID 的新对象。应用程序是 RESTful,正在插入的数据是来自客户端的 JSON。新记录已正确插入,但 return 值中的子对象仍然仅包含 ID。是否有 Spring 与 refresh() 等效的引导?我是否需要创建一个可以访问 EntityManager 的自定义存储库?

我应该补充一点,在插入后使用 findOne() 并不能解决问题。子对象仍然不完整。但是,当我 select 在单独的 REST 调用中插入记录时,该对象与所有子对象一起完成。

代码如下。如您所见,存储库和服务方法非常简单,以至于我不知道该做什么。刷新看起来像什么 Spring 引导应该自动处理。

注意:SO 评论表单正在从 CrudRepository 和一些 return 类型。

Volume.java



    @Entity
    @JsonSerialize(using = VolumeSerializer.class)
    public class Volume implements Protectable {

        public static final int ATTACHED_STATE = 1;
        public static final int DETACHED_STATE = 0;
        public static final int SUSPENDED_STATE = -1;

        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private long id;
        private String name;
        @OneToOne
        private VolumeType type;
        private int size;
        @OneToOne
        private UserGroup userGroup;
        @OneToOne
        private Region region;
        @OneToOne
        private State state;
        @OneToOne
        private Status status;
        @OneToOne
        private Alarm alarmStatus;
        private long createdDate;
        @Column(columnDefinition = "bigint default 0")
        private long lastModifiedDate = 0;
        @Column(columnDefinition = "tinyint default 0")
        private boolean monitoringEnabled = false;
        @Column(columnDefinition = "tinyint default 0")
        private boolean encrypted = false;
        @OneToOne
        private Snapshot snapshot;
        @ManyToOne
        private Instance instance;
    /*
    after this there are constructors, a long list of getters for the properties above
    a static Builder class and @PrePersist and @PreUpdate methods
    */
    ...

VolumeRepository.java


    /**
     * For accessing Volume information
     */
    public interface VolumeRepository extends CrudRepository {
       List findByUserGroupId(long groupId);
    }

VolumeService.java



    @Transactional
    public Iterable save(List volumes) {
       return this.repository.save(volumes);
    }

VolumeRouter.java

这是一个@RestController。



    @RequestMapping(value="/volumes", method=RequestMethod.POST)
    public Iterable create(@RequestBody List volumes) {
        return this.service.save(volumes);
    }

application.properties

(ddl-auto 设置用于开发)



    # Datasource configuration
    spring.datasource.url=****
    spring.datasource.username=****
    spring.datasource.password=****
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    spring.datasource.time-between-eviction-runs-millis=34000
    spring.datasource.min-evictable-idle-time-millis=55000
    spring.datasource.min-idle=0


    # Hibernate settings
    spring.jpa.hibernate.ddl-auto=create-drop
    spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
    spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.ImprovedNamingStrategy

VolumeServiceIT.java

JSON 字符串向您展示了客户的期望 能够发送到服务。 UserGroup 对象只包含 从客户端发送的 ID,但它应该是完整的 UserGroup 目的。其他子对象也是如此。



    @Test
    public void createVolume() throws Exception {
        String json = "[{" +
                    "\"name\": \"Test Volume\", " +
                    "\"size\": 24, " +
                    "\"monitoringEnabled\": true, " +
                    "\"encrypted\": true, " +
                    "\"state\": { \"id\": 1 }, " +
                    "\"userGroup\": { \"id\": 1 }, " +
                    "\"region\": { \"id\": 1 }, " +
                    "\"type\": { \"id\": 1 }" +
                "}]";

        mvc.perform(MockMvcRequestBuilders.post("/volumes")
                .principal(token)
                .contentType(MediaType.APPLICATION_JSON)
                .content(json))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$[0].id", is(4)))
                .andExpect(jsonPath("$[0].name", is("Test Volume")))
                .andExpect(jsonPath("$[0].size", is(24)))
                .andExpect(jsonPath("$[0].userGroup.id", is(1)))
                .andExpect(jsonPath("$[0].userGroup.name", is("myGroup")))
                .andExpect(jsonPath("$[0].userGroup.users", isEmptyOrNullString()))
                .andExpect(jsonPath("$[0].type.id", is(1)))
                .andExpect(jsonPath("$[0].type.value", is("SSD")))
                .andExpect(jsonPath("$[0].region.id", is(1)))
                .andExpect(jsonPath("$[0].region.value", is("us-west")))
                .andExpect(jsonPath("$[0].state.id", is(1)))
                .andExpect(jsonPath("$[0].state.value", is("on")))
                .andExpect(jsonPath("$[0].status", isEmptyOrNullString()))
                        //.andExpect(jsonPath("$.createdDate", is(1425240151000L)))
                .andExpect(jsonPath("$[0].monitoringEnabled", is(true)))
                .andExpect(jsonPath("$[0].encrypted", is(true)));
    }

在咨询了同事后,我将EntityManager注入到上面的VolumeService中,并在Volume上调用了refresh。



        @PersistenceContext
        private EntityManager entityManager;

    ...

        @Transactional
        public Iterable save(List volumes) {
            Iterable result = this.repository.save(volumes);

            for (Volume volume : result){
                entityManager.refresh(volume);
            }
            return result;
        }

请注意,虽然我更喜欢此选项,但我的同事建议获取子对象,将它们添加到新卷,然后保存卷,这显然会起作用。但是,我不想那样做。