我有这两个简单的实体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
(春季(:
@SuppressWarnings("unused")
@Repository
public interface SomethingRepository extends JpaRepository<Something,Long> {
}
通过 REST 控制器和 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
包含持久化的实体,但没有字段property
的字段,验证(它们是null
(:
输出 JSON:
{
"id": 1,
"name": "MyName",
"owner": "MySelf",
"property": {
"id": 1,
"shape": null,
"color": null,
"dimension": null
}
}
我希望它们在保存操作后得到验证/返回。
要解决此问题,我必须在 REST 控制器中注入/声明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;
}
}
使用此解决方法,我获得了预期的已保存内容(使用正确的 JSON(:
{
"id": 4,
"name": "MyName",
"owner": "MySelf",
"property": {
"id": 1,
"shape": "Rectangle",
"color": "Red",
"dimension": 50
}
}
是否有一种自动方法/注释,带有JPA,Spring或Hibernate,以便拥有"完整"的持久实体?
我想避免在每个 REST 或服务类中声明EntityManager
,或者我想避免每次想要新的刷新持久实体时调用.findOne(Long)
方法。
无需在每个资源中定义EntityManager
,而是可以通过创建自定义 JpaRepository 来定义一次。参考
然后直接在每个存储库中使用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 引导应用程序类中启用自定义 JPAR实体
@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 中使用刷新(假设某物是实体(
@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;
}
现在,您需要仔细合并您发送的每个属性。在您的情况下,如果您发送null
以供property
,则应决定是否应取消@ManyToOne
引用。因此,这取决于您当前的应用程序业务逻辑要求。
更新
如果您确保始终发回之前获取的相同实体,则可以使用merge
.
em.merge(result);
但是您的property
属性只是一个 id,而不是实际的子实体,因此您必须在服务图层中自行解析该属性。
在 Spring Boot JpaRepository 中:
如果我们的修改查询更改了持久性上下文中包含的实体,则此上下文将过时。
为了从数据库中获取具有最新记录的实体。
使用 @Modifying(清除自动 = true(
@Modifying注释具有 clearAutomatic 属性,该属性定义在执行修改查询后是否应清除基础持久性上下文。
例:
@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;
现在结果将从数据库加载整个属性对象
当您持久化实体时,它将处于托管状态,因此如果您只是调用something.getProperty();
它就会从数据库加载并填充something
实体的property
值
public Something save(Something something) {
em.persist(something);
something.getProperty();
return something;
}
所以通常当你有多对一的关系时,应该自动获取。如果不调用实体中对象的 getter,也会通过触发新的 DB Find 请求来填充它们。