簡體   English   中英

保存后刷新並獲取實體(JPA/Spring Data/Hibernate)

[英]Refresh and fetch an entity after save (JPA/Spring Data/Hibernate)

我有這兩個簡單的實體SomethingProperty 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 參考

然后直接在每個存儲庫中使用EntityManagerrefresh

請參考以下示例:

自定義存儲庫接口

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM