[英]Refresh and fetch an entity after save (JPA/Spring Data/Hibernate)
我有這兩個簡單的實體Something
和Property
。 Something
實體與Property
具有多對一的關系,因此當我創建新的Something
行時,我分配了一個現有的Property
。
某物:
@Entity
@Table(name = "something")
public class Something implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
@Column(name = "owner")
private String owner;
@ManyToOne
private Property property;
// getters and setters
@Override
public String toString() {
return "Something{" +
"id=" + getId() +
", name='" + getName() + "'" +
", owner='" + getOwner() + "'" +
", property=" + getProperty() +
"}";
}
財產:
@Entity
@Table(name = "property")
public class Property implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "shape")
private String shape;
@Column(name = "color")
private String color;
@Column(name = "dimension")
private Integer dimension;
// getters and setters
@Override
public String toString() {
return "Property{" +
"id=" + getId() +
", shape='" + getShape() + "'" +
", color='" + getColor() + "'" +
", dimension='" + getDimension() + "'" +
"}";
}
}
這是SomethingRepository
(Spring):
@SuppressWarnings("unused")
@Repository
public interface SomethingRepository extends JpaRepository<Something,Long> {
}
通過 REST controller 和 JSON,我想創建一個新的Something
:
@RestController
@RequestMapping("/api")
public class SomethingResource {
private final SomethingRepository somethingRepository;
public SomethingResource(SomethingRepository somethingRepository) {
this.somethingRepository = somethingRepository;
}
@PostMapping("/somethings")
public Something createSomething(@RequestBody Something something) throws URISyntaxException {
Something result = somethingRepository.save(something);
return result;
}
}
這是輸入中的 JSON( property
id
1 是數據庫中的現有行):
{
"name": "MyName",
"owner": "MySelf",
"property": {
"id": 1
}
}
問題是:在方法.save(something)
之后,變量result
包含持久實體,但沒有 field property
的字段,經過驗證(它們是null
):
Output JSON:
{
"id": 1,
"name": "MyName",
"owner": "MySelf",
"property": {
"id": 1,
"shape": null,
"color": null,
"dimension": null
}
}
我希望它們在保存操作后得到驗證/返回。
為了解決這個問題,我必須在 REST controller 中注入/聲明EntityManager
,並調用EntityManager.refresh(something)
方法(或者我必須調用.findOne(something.getId())
方法來獲得完整的持久化實體):
@RestController
@RequestMapping("/api")
@Transactional
public class SomethingResource {
private final SomethingRepository somethingRepository;
private final EntityManager em;
public SomethingResource(SomethingRepository somethingRepository, EntityManager em) {
this.somethingRepository = somethingRepository;
this.em = em;
}
@PostMapping("/somethings")
public Something createSomething(@RequestBody Something something) throws URISyntaxException {
Something result = somethingRepository.save(something);
em.refresh(result);
return result;
}
}
使用此解決方法,我已經保存了預期的 entith(使用正確的 JSON):
{
"id": 4,
"name": "MyName",
"owner": "MySelf",
"property": {
"id": 1,
"shape": "Rectangle",
"color": "Red",
"dimension": 50
}
}
是否有使用 JPA 或 Spring 或 Hibernate 的自動方法/注釋,以便擁有“完整”的持久實體?
我想避免在每個 REST 或服務 class 中聲明EntityManager
,或者我想避免每次我想要新的刷新的持久實體時調用.findOne(Long)
方法。
您可以通過創建自定義JpaRepository 來定義一次,而不是在每個資源中定義EntityManager
。 參考
然后直接在每個存儲庫中使用EntityManager
的refresh
。
請參考以下示例:
自定義存儲庫接口
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.NoRepositoryBean;
import java.io.Serializable;
@NoRepositoryBean
public interface CustomRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
void refresh(T t);
}
自定義存儲庫實現
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import java.io.Serializable;
public class CustomRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID>
implements CustomRepository<T, ID> {
private final EntityManager entityManager;
public CustomRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
this.entityManager = entityManager;
}
@Override
@Transactional
public void refresh(T t) {
entityManager.refresh(t);
}
}
在 Spring Boot 應用程序類中啟用自定義 JPARepository
@SpringBootApplication
@EnableJpaRepositories (repositoryBaseClass = CustomRepositoryImpl.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
你的東西存儲庫
public interface SomethingRepository extends CustomRepository<Something, Long> {
}
在SomethingResource中直接使用Refresh (假設某物是一個實體)
@RestController
@RequestMapping("/api")
@Transactional
public class SomethingResource {
private final SomethingRepository somethingRepository;
public SomethingResource(SomethingRepository somethingRepository) {
this.somethingRepository = somethingRepository;
}
@PostMapping("/somethings")
public Something createSomething(@RequestBody Something something) throws URISyntaxException {
Something result = somethingRepository.save(something);
somethingRepository.refresh(result);
return result;
}
}
這還不夠:
Something result = somethingRepository.save(something);
您需要手動合並傳入的實體:
Something dbSomething = somethingRepository.findOne(
Something.class, something.getId()
);
dbSomething.setName(something.getName());
dbSomething.setOwner(something.getOwner());
somethingRepository.save(dbSomething);
由於property
屬性使用默認的FetchType.EAGER
,因此實體應該初始化property
屬性。
但是,從 REST 控制器調用存儲庫兩次很奇怪。 你應該有一個服務層,在@Transactional
服務方法中完成所有這些。 這樣,您不需要重新保存實體,因為它已經被管理。
@Transactional
public Something mergeSomething(Something something) {
Something dbSomething = somethingRepository.findOne(
Something.class, something.getId()
);
dbSomething.setName(something.getName());
dbSomething.setOwner(something.getOwner());
return dbSomething;
}
現在,您需要仔細合並您發送的每個屬性。 在您的情況下,如果您為property
發送null
,您應該決定是否應取消@ManyToOne
引用。 因此,這取決於您當前的應用程序業務邏輯需求。
如果您確保始終發回之前獲取的同一實體,則可以使用merge
。
em.merge(result);
但是您的property
屬性只是一個 id,而不是實際的子實體,因此您必須自己在 Service 層解決這個問題。
在 Spring Boot JpaRepository 中:
如果我們的修改查詢更改了持久化上下文中包含的實體,那么這個上下文就會過時。
為了從具有最新記錄的數據庫中獲取實體。
使用@Modifying(clearAutomatically = true)
@Modifying 注釋具有 clearAutomatically 屬性,該屬性定義在執行修改查詢后是否應清除底層持久性上下文。
示例:
@Modifying(clearAutomatically = true)
@Query("UPDATE NetworkEntity n SET n.network_status = :network_status WHERE n.network_id = :network_id")
int expireNetwork(@Param("network_id") Integer network_id, @Param("network_status") String network_status);
我會用使用session.load
生成的代理替換該屬性
something.setProperty(session.load(Property.class, something.getProperty().getId()))
Something result = somethingRepository.save(something);
return result;
現在結果將從數據庫加載整個屬性 object
當您持久化實體時,它將處於托管狀態,因此如果您只調用something.getProperty();
它從數據庫加載並填充something
實體的property
值
public Something save(Something something) {
em.persist(something);
something.getProperty();
return something;
}
所以通常當你有應該自動獲取的多對一關系時。 如果不調用實體中對象的 getter 也會通過觸發新的 DB Find 請求來填充它們。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.