如何在 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;
}
请注意,虽然我更喜欢此选项,但我的同事建议获取子对象,将它们添加到新卷,然后保存卷,这显然会起作用。但是,我不想那样做。
这个问题类似于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;
}
请注意,虽然我更喜欢此选项,但我的同事建议获取子对象,将它们添加到新卷,然后保存卷,这显然会起作用。但是,我不想那样做。