[英]Hibernate get parent's id from child entity
很多人都發布了非常相似的問題,但我似乎無法找到解決我的情況的問題。
我有一個父實體Dnar
和一個子實體WorkLocationSuburb
。 Dnar
具有一對多的WorkLocationSuburbs
。
通常,我會將這種關系定義如下:
孩子
@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 抱怨其他問題的情況下將此信息添加到WorkLocationSuburb
? 看來我需要一些代碼,例如:
@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 的建議
我的代碼現在是這樣的:
孩子
@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
}
當我嘗試保存Dnar
具有WorkLocationSuburb
連接,我看到的事件順序如下:
Dnar
插入到數據庫中@PostConstuct
並且WorkLocationSuburb
獲得對父Dnar
的引用。 父Dnar
已正確分配 ID。WorkLocationSuburb
,但導致以下異常:org.hibernate.exception.ConstraintViolationException: ORA-01400: cannot insert NULL into ("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 查詢始終適用於實體及其持久字段/屬性。 永遠不要放在桌子和它們的列上。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.