java - Hibernate从子实体获取父实体的id

标签 java hibernate jpa-2.0 spring-data-jpa

很多人都发布了非常类似的问题,但我似乎找不到能解决我的情况的问题。

我有一个父实体 Dnar 和一个子实体 WorkLocationSuburbDnar 具有一对多的 WorkLocationSuburbs

通常,我会如下定义这种关系:

child

@Entity
@Table(name = "req_work_suburb", schema = SCHEMA)
public class WorkLocationSuburb {

  @Id
  @GeneratedValue(generator = "req_work_suburb_seq")
  @GenericGenerator(strategy="sequence", name="req_work_suburb_seq", parameters={@Parameter(name="sequence", value=SCHEMA+".req_work_suburb_seq")})
  @Column(name = "id", nullable = false)
  private Long id;

  @Column(name="suburb", length=255, nullable=false)
  private String suburb;

  //remainder omitted
}

家长

@Entity
@Table(name = "req_dnar", schema = SCHEMA)
@Inheritance(strategy = JOINED)
@DiscriminatorColumn(name = "form_type", discriminatorType = STRING, length = 64)
public abstract class Dnar {

  @Id
  @GeneratedValue(generator = "req_dnar_seq")
  @GenericGenerator(strategy = "sequence", name = "req_dnar_seq", parameters = { @Parameter(name = "sequence", value = SCHEMA + ".req_dnar_seq") })
  @Column(name = C_DNAR_ID, nullable = false)
  private Long dnarId;

  @Column(name = "original_id")
  private Long originalId;


  @OneToMany(cascade = ALL, fetch = EAGER, orphanRemoval=true)
  @JoinColumn(name = "dnar_id", nullable=false)
  private Set<WorkLocationSuburb> workLocationSuburbs;

  //remainder omitted
}

上面的示例有效,当我手动查询 req_work_suburb 表时,我可以看到它有一个 dnar_id 列,并且该列已正确填充。到目前为止一切都很好!

在某些查询代码中,我创建了一个依赖于 req_work_suburb.dnar_id 值的条件查询:

  private static Specification<SubmittedDnarSummary> suburbIs(final String suburb) {
    return new Specification<SubmittedDnarSummary>(){
      @Override
      public Predicate toPredicate(Root<SubmittedDnarSummary> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
        Subquery<Long> subquery = query.subquery(Long.class);
        Root<WorkLocationSuburb> suburbRoot = subquery.from(WorkLocationSuburb.class);
        return cb.in(root.get("dnarId")).value(subquery.select(suburbRoot.<Long>get("dnarId")).where(cb.equal(suburbRoot.get("suburb"), suburb)));
      }
    };
  }

当我执行上述代码时,出现以下异常:

org.springframework.dao.InvalidDataAccessApiUsageException: Unable to resolve attribute [dnarId] against path; nested exception is java.lang.IllegalArgumentException: Unable to resolve attribute [dnarId] against path
         at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:301)
         at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:106)
         at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:403)
         at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:58)
         at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
         at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:163)
         at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
         at org.springframework.data.jpa.repository.support.LockModeRepositoryPostProcessor$LockModePopulatingMethodIntercceptor.invoke(LockModeRepositoryPostProcessor.java:91)
         at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
         at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
         at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
         at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
         at $Proxy253.findAll(Unknown Source)
         at au.com.wpcorp.sys.dnar.spring.manager.impl.SearchManagerImpl.search(SearchManagerImpl.java:66)
         at au.com.wpcorp.sys.dnar.spring.mvc.controller.search.DnarSearchController.results(DnarSearchController.java:97)
         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
         at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
         at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
         at java.lang.reflect.Method.invoke(Method.java:597)
         at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:213)
         at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
         at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
         at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
         at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
         at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
         at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
         at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
         at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
         at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
         at javax.servlet.http.HttpServlet.service(HttpServlet.java:668)
         at javax.servlet.http.HttpServlet.service(HttpServlet.java:770)
         at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1550)
         at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:281)
         at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
         at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655)
         at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595)
         at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:161)
         at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:331)
         at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231)
         at com.sun.enterprise.v3.services.impl.ContainerMapper$AdapterCallable.call(ContainerMapper.java:317)
         at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:195)
         at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:860)
         at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:757)
         at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1056)
         at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:229)
         at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
         at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
         at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
         at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
         at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
         at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
         at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
         at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
         at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
         at java.lang.Thread.run(Thread.java:662)
Caused by: java.lang.IllegalArgumentException: Unable to resolve attribute [dnarId] against path
         at org.hibernate.ejb.criteria.path.AbstractPathImpl.unknownAttribute(AbstractPathImpl.java:118)
         at org.hibernate.ejb.criteria.path.AbstractPathImpl.locateAttribute(AbstractPathImpl.java:223)
         at org.hibernate.ejb.criteria.path.AbstractPathImpl.get(AbstractPathImpl.java:194)
         at au.com.wpcorp.sys.dnar.spring.manager.impl.SearchManagerImpl$3.toPredicate(SearchManagerImpl.java:141)
         at org.springframework.data.jpa.repository.support.SimpleJpaRepository.applySpecificationToCriteria(SimpleJpaRepository.java:480)
         at org.springframework.data.jpa.repository.support.SimpleJpaRepository.getQuery(SimpleJpaRepository.java:436)
         at org.springframework.data.jpa.repository.support.SimpleJpaRepository.getQuery(SimpleJpaRepository.java:421)
         at org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll(SimpleJpaRepository.java:303)
         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
         at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
         at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
         at java.lang.reflect.Method.invoke(Method.java:597)
         at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:334)
         at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:319)
         at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
         at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
         at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
         at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155)
         ... 49 more

它似乎失败了,因为 Hibernate 在 WorkLocationSuburb 上找不到名为 dnarId 的属性。我的问题是,如何将此信息添加到 WorkLocationSuburb 而不让 Hibernate 提示其他问题?看来我需要一些代码,例如:

@Entity
@Table(name = "req_work_suburb", schema = SCHEMA)
public class WorkLocationSuburb {

  @Id
  @GeneratedValue(generator = "req_work_suburb_seq")
  @GenericGenerator(strategy="sequence", name="req_work_suburb_seq", parameters={@Parameter(name="sequence", value=SCHEMA+".req_work_suburb_seq")})
  @Column(name = "id", nullable = false)
  private Long id;

  @Column(name="suburb", length=255, nullable=false)
  private String suburb;

  @Column(name="dnar_id", nullable=false)  // column definition added here
  private Long dnarId;

  //remainder omitted
}

有谁知道如何使上述附加列发挥作用?此列必须自动填充父级的dnarId

谢谢, 穆埃尔。


更新了 JB Nizet 的建议

我的代码现在是这样的:

child

@Entity
@Table(name = "req_work_suburb", schema = SCHEMA)
public class WorkLocationSuburb {

  @Id
  @GeneratedValue(generator = "req_work_suburb_seq")
  @GenericGenerator(strategy="sequence", name="req_work_suburb_seq", parameters={@Parameter(name="sequence", value=SCHEMA+".req_work_suburb_seq")})
  @Column(name = "id", nullable = false)
  private Long id;

  @Column(name="suburb", length=255, nullable=false)
  private String suburb;

  @ManyToOne
  @JoinColumn(name="dnar_id", nullable=false)
  private Dnar dnar;

  //remainder omitted
}

家长

@Entity
@Table(name = "req_dnar", schema = SCHEMA)
@Inheritance(strategy = JOINED)
@DiscriminatorColumn(name = "form_type", discriminatorType = STRING, length = 64)
public abstract class Dnar {

  @Id
  @GeneratedValue(generator = "req_dnar_seq")
  @GenericGenerator(strategy = "sequence", name = "req_dnar_seq", parameters = { @Parameter(name = "sequence", value = SCHEMA + ".req_dnar_seq") })
  @Column(name = C_DNAR_ID, nullable = false)
  private Long dnarId;

  @Column(name = "original_id")
  private Long originalId;

  @OneToMany(cascade = ALL, fetch = EAGER, orphanRemoval=true, mappedBy="dnar")
  private Set<WorkLocationSuburb> workLocationSuburbs = new LinkedHashSet<WorkLocationSuburb>();


  @PostPersist
  void setChildRefs() {
    for (WorkLocationSuburb suburb : workLocationSuburbs) {
      suburb.setDnar(this);
    }
  }

  //remainder omitted
}

当我尝试保存附加了 WorkLocationSuburbDnar 时,我看到以下事件顺序:

  1. Dnar 已插入数据库
  2. @PostConstuct 被调用,并且 WorkLocationSuburb 获取对父级 Dnar 的引用。父级 Dnar 已正确分配 ID。
  3. 尝试插入子 WorkLocationSuburb,但会导致以下异常:org.hibernate.exception.ConstraintViolationException:ORA-01400:无法将 NULL 插入 ("DNAR"."REQ_WORK_SUBURB"。 “DNAR_ID”)`

我还做错了什么? :)

最佳答案

您只需将关联设置为双向即可:

public class WorkLocationSuburb {
    @ManyToOne
    @JoinColumn
    private Dnar parentDnar;
    // ...
}

public class Dnar {
    @OneToMany(mappedBy = "parentDnar", ...)
    private Set<WorkLocationSuburb> suburbs;
    // ...
}

请注意,在双向 OneToMany 关联中,关联的拥有方始终是多方(没有 mappedBy 属性的一方)。这意味着 Hibernate 将仅考虑 WorkLocationSuburb.parentDnar 字段来决定关联是否存在。因此,这意味着每次将郊区添加到 dnar 时,还必须设置郊区的 dnar 字段:

public void addSuburb(WorkLocationSuburb suburb) {
    this.suburbs.add(suburb);
    suburb.setParentDnar(this);
}

同样,如果您从 dnar 中删除郊区,则应将该郊区的 ParentDnar 设置为 null。

关于查询,您现在可以在查询中使用 parentDnar 字段。请记住,JPA 查询始终适用于实体及其持久字段/属性。切勿在表格及其列上。

关于java - Hibernate从子实体获取父实体的id,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13509675/

相关文章:

java - 如何添加新的项目目录

java - 无法在未调用 Looper.prepare() 的线程内创建处理程序

Java InputStream 读取方法为 NFS 安装位置中的文件返回 ASCII 'NUL' 字符

java - 什么是数据库 session ?

java - org.hibernate.MappingException : Could not determine type for: java. util.Map

java - Jpa - 根据列类型连接不同的表

java - 运行 Java 集成测试时未设置环境变量

java - 全局设置 hibernate 'default-cascade' 属性

java - Spring hibernate : No matching bean of type org. hibernate.SessionFactory

java - 查询hibernate中向经理汇报的总人数