保存后刷新并获取实体(JPA / Spring Data / Hibernate)
我有这两个简单的实体Something
and 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 { }
通过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
包含持久化实体,但没有field property
字段,validation(它们为null
):
输出JSON:
{ "id": 1, "name": "MyName", "owner": "MySelf", "property": { "id": 1, "shape": null, "color": null, "dimension": null } }
我希望在保存操作后validation/返回它们。
要解决这个问题,我必须在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; } }
通过这种解决方法,我得到了预期的保存entith(使用正确的JSON):
{ "id": 4, "name": "MyName", "owner": "MySelf", "property": { "id": 1, "shape": "Rectangle", "color": "Red", "dimension": 50 } }
是否有自动方法/注释,使用JPA或Spring或Hibernate,以获得“完整”持久化实体?
我想避免在每个REST或Service类中声明EntityManager,或者我希望每次我想要新刷新的持久化实体时都避免调用.findOne(Long)方法。
非常感谢Andrea
这还不够:
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.refresh(result);
但是您的property
属性只是一个id,而不是一个实际的子实体,因此您必须自己在Service层中解决它。
您可以通过创建Custom JpaRepository来定义一次,而不是在每个资源中定义EntityManager
。 参考
然后直接在每个存储库中使用EntityManager
的refresh
。
请参考以下示例:
CustomRepository接口
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.repository.NoRepositoryBean; import java.io.Serializable; @NoRepositoryBean public interface CustomRepository extends JpaRepository { void refresh(T t); }
CustomRepository实现
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 extends SimpleJpaRepository implements CustomRepository { 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 Application Class中启用自定义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 { }
直接在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.getProperty();
它从数据库加载并填充something
实体的property
值
public Something save(Something something) { em.persist(something); something.getProperty(); return something; }
所以通常当你有多对一的关系应该自动获取。 如果没有调用实体中对象的getter,也会通过触发新的DB Find请求来填充它们。
- Hibernate使用新生成的ID将对象值复制到新对象中
- Hibernate从4.3.x迁移到5.x,用于方法org.hibernate.cfg.Configuration.getClassMapping(className)
- 如何防止Hibernate删除JSONpost中不存在的子对象?
- 使用Hibernate / JPA和JDK Date进行不需要的自动时区转换
- Hibernate映射具有空值的复合键
- 您是否需要数据库事务来读取数据?
- 在自动触发的默认侦听器之前,激活Hibernate自定义事件侦听器
- java.math.BigInteger无法强制转换为java.lang.Long
- 未能懒惰地初始化角色集合,..无法初始化代理 – 没有会话 – JPA + SPRING