【问题标题】:Can one-to-one shared primary key mapping in Hibernate be optional/null?Hibernate 中的一对一共享主键映射可以是可选的/空的吗?
【发布时间】:2012-05-03 00:57:30
【问题描述】:

我在网上看到了很多使用 Hibernate 注释进行一对一共享主键映射的示例/教程。但是,在我看到的所有示例中,映射似乎不是可选的,或者如果它是可选的,则它不能位于主键上。我有以下情况,我在 Company 表之间有一个一对一的主键映射 和一个 CompanyDocs 表。 CompanyDocs 表是一个弱实体,即虽然存在 Company 对象,但它不一定会从一开始就链接 CompanyDocs 实体,但是这可能会在以后创建。

Company 对象由外部应用程序创建和持久化,但使用 Hibernate 应用程序进行更新。 这就是我所拥有的..(请注意这不是实际示例)

Company table
- companyid (Pk)
- name
- desc
- test1
- test2

CompanyDocs table
- docsid (Pk) (Fk) 
- detail1
- detail2
- detail3

模型类

Company.java
@Entity
@Table(name = "company", schema = "public")   
public class Company implements java.io.Serializable {

  private String companyid
  private CompanyDocs companydocs;

  @Id
  @Column(name = "companyid", unique = true, nullable = false, length = 20)
  public String getCompanyid() {
    return this.companyid;
}

  public void setCompanyid(String companyid) {
    this.companyid = companyid;
}

  @OneToOne(fetch = FetchType.LAZY, mappedBy = "companydocs")
  @NotFound(action = NotFoundAction.IGNORE)
  @PrimaryKeyJoinColumn(name = "companyid", referencedColumnName = "docsid")
  public CompanyDocs getCompanydocs() {
    return this.companydocs;
}

  public void setCompanydocs(CompanyDocs companydocs) {
    this.companydocs = companydocs;
} 

}

CompanyDocs.java

@Entity
@Table(name = "companydocs", schema = "public")
public class CompanyDocs implements java.io.Serializable {

    private String docsid;
    private Company company;

    @GenericGenerator(name = "generator", strategy = "foreign", parameters =  
    @Parameter(name = "property", value = "company"))
    @Id
    @GeneratedValue(generator = "generator")
    @Column(name = "docsid", unique = true, nullable = false, length = 20)
    public String getDocsid() {
        return this.docsid;
    }

    public void setDocsid(String docsid) {
        this.docsid = docsid;
    }


    @PrimaryKeyJoinColumn(name = "docsid", referencedColumnName = "companyid")      
    @OneToOne(fetch = FetchType.LAZY)
    @NotFound(action = NotFoundAction.IGNORE)
    public Company getCompany() {
        return this.company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }

公司DAOImpl.java

public Company getCompany(String companyid) {
Criteria c = sessionFactory.getCurrentSession().createCriteria(Company.class)
                 .add(Restrictions.eq("companyid", companyid));     
    try {       
        return (Company) c.list().get(0);
    }
    catch(IndexOutOfBoundsException e) {        
        logger.info("GetCompany threw exception " +e);
        return null;
    }       

}

public void storeCompany(Company c) {
sessionFactory.getCurrentSession().saveOrUpdate(c);
 }

public void storeCompanyDocs(CompanyDocs cd) {
sessionFactory.getCurrentSession().saveOrUpdate(cd);
 }

public CompanyDocs getCompanyDocs(String companyid) {       
    try{
       Criteria c = sessionFactory.getCurrentSession()
      .createCriteria(CompanyDocs.class).add(Restrictions.eq("companyid", companyid));
        logger.info("getCompanyDocsreturned"+c.list().get(0));
        return (CompanyDocs)c.list().get(0);            

    }catch(IndexOutOfBoundsException e){
        logger.info("getCompanyDocs threw exception " +e);
        return null;            
    }

}

CompanyServiceImpl.java

 @Transactional(propagation = Propagation.REQUIRED, readOnly= false,       
 noRollbackFor=IllegalArgumentException.class)
 @ExceptionHandler(NullPointerException.class)

 public CompanyResponse updateCompany(Service parameters) throws SOAPException { 
 LOG.info("In updateCompany");
 Company c = companyDao.getCompany(parameters.getId());
 //CompanyDocs cd = companyDao.getCompanyDocs(parameters.getId());
 CompanyDocs cd = c.getCompanyDocs();       
  LOG.info("CompanyDocs object is:- " + cd);
    if(cd == null)
      {
        cd = new CompanyDocs(parameters.getId());
        c.setCompanyDocs(cd);
            cd.setCompany(c);
            LOG.info("CompanyDocs object created new :- " + cd);
            companyDao.storeCompanyDocs(cd);
        LOG.info("CompanyDocs object stored :- " + cd.getDocsid());     
      } 

        c.setDetail1();
        c.setDetail2();
        c.setDetail3();
        .............
        companyDao.storeCompany(c)
        LOG.info("Stored Company");
        LOG.info("CompanyService Response Return------:")
        return new CompanyResponse();

 }

我的日志文件打印了这个。

   2012-05-02 22:57:18,951 INFO [au.com.CompanyServiceImpl] - <In updateCompany>
   2012-05-02 22:57:18,975 INFO [au.com.CompanyDAOImpl] - <getCompany returned        
   au.com.Company@2738ef1d>[au.com.CompanyServiceImpl] - <CompanyDocs object is:- null>
   2012-05-02 22:57:18,991 INFO [au.com.CompanyServiceImpl] - <CompanyDocs object 
   created new :- au.com.CompanyDocs@373ee92>
   2012-05-02 22:57:18,995 INFO [au.com.CompanyServiceImpl] - <CompanyDocs object 
   updated :- CO12345>
   2012-05-02 22:57:19,338 INFO [au.com.CompanyDAOImpl] - <Stored Company >
   2012-05-02 22:57:19,338 INFO [au.com.CompanyServiceImpl] - <CompanyService 
   Response  Return------:>
   2012-05-02 22:57:19,498 ERROR [org.hibernate.jdbc.AbstractBatcher] - <Exception    
   executing batch: >
   org.hibernate.StaleStateException: Batch update returned unexpected row count from  
   update [0]; actual row count: 0; expected: 1 at 
   org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:61)
   at   
   org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:46)
at org.hibernate.jdbc.BatchingBatcher.checkRowCounts(BatchingBatcher.java:68)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:246)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:168)
at org.hibernate.event.def.AbstractFlushingEventListener
    .performExecutions(AbstractFlushingEventListener.java:298)
at org.hibernate.event.def.DefaultFlushEventListener.
    onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at org.springframework.orm.hibernate3.HibernateTransactionManager.
    doCommit(HibernateTransactionManager.java:656)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.
    processCommit(AbstractPlatformTransactionManager.java:754)
at org.springframework.transaction.support.AbstractPlatformTransactionManager
     .commit(AbstractPlatformTransactionManager.java:723)
at org.springframework.transaction.interceptor.TransactionAspectSupport
    .commitTransactionAfterReturning(TransactionAspectSupport.java:393)
at org.springframework.transaction.interceptor.TransactionInterceptor
    .invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation
    .proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.
    invoke(JdkDynamicAopProxy.java:202)
at $Proxy28.login(Unknown Source)
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.apache.cxf.service.invoker.AbstractInvoker.
    performInvocation(AbstractInvoker.java:173)
at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:89)
at org.apache.cxf.jaxws.JAXWSMethodInvoker.invoke(JAXWSMethodInvoker.java:60)
at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:75)
at org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.
     run(ServiceInvokerInterceptor.java:58)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at org.apache.cxf.workqueue.SynchronousExecutor.
       execute(SynchronousExecutor.java:37)
at org.apache.cxf.interceptor.ServiceInvokerInterceptor.
     handleMessage(ServiceInvokerInterceptor.java:106)
at org.apache.cxf.phase.PhaseInterceptorChain.
      doIntercept(PhaseInterceptorChain.java:255)
at org.apache.cxf.transport.ChainInitiationObserver.
    onMessage(ChainInitiationObserver.java:113)
at org.apache.cxf.transport.servlet.ServletDestination.
    invoke(ServletDestination.java:97)
at org.apache.cxf.transport.servlet.ServletController.
      invokeDestination(ServletController.java:461)
at org.apache.cxf.transport.servlet.ServletController.
     invoke(ServletController.java:188)
at org.apache.cxf.transport.servlet.AbstractCXFServlet.
     invoke(AbstractCXFServlet.java:148)
at org.apache.cxf.transport.servlet.AbstractHTTPServlet.
     handleRequest(AbstractHTTPServlet.java:179)
at org.apache.cxf.transport.servlet.
     AbstractHTTPServlet.doPost(AbstractHTTPServlet.java:103)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
at org.apache.cxf.transport.servlet.AbstractHTTPServlet.
    service(AbstractHTTPServlet.java:159)
at org.apache.catalina.core.ApplicationFilterChain.
    internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.
    doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.
      invoke(StandardWrapperValve.java:233) 
at org.apache.catalina.core.StandardContextValve.
     invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.
    invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:859)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.
     process(Http11Protocol.java:588)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
at java.lang.Thread.run(Thread.java:662)
   2012-05-02 22:57:19,501
  ERROR [org.hibernate.event.def.AbstractFlushingEventListener]     
  -   <Could not synchronize database state with session>

我尝试过使用@non annotation,optional = false/true,也尝试过使用单向映射并使用标准加载 companydocs 对象。但似乎 Hibernate 总是排除现有公司的 CompanyDocs 对象..

奇怪的是,当我们使用早期版本的 hibernate 并使用映射文件而不是注释时,它可以正常工作。这是一个共享键映射,但单向,因此公司 hbm 文件没有 CompanyDocs 对象的映射。 我正在使用 hibernate 3.3.1.GA.jar 进行注释。是不是我做错了什么,或者这在新版本的 Hibernate 中不再可能?

【问题讨论】:

    标签: java hibernate postgresql annotations one-to-one


    【解决方案1】:

    问题在于 Hibernate 发出了更新,而不是 companydocs 对象的插入命令。这是因为如果对象的主键为空,那么 Hibernate 将执行插入,如果它不为空,它将执行更新,在我的情况下,因为我在实现类中设置键,如下所示,它正在执行更新.

    原始代码

    if(cd == null)
      {
        cd = new CompanyDocs(parameters.getId());
        c.setCompanyDocs(cd);
        cd.setCompany(c);
        LOG.info("CompanyDocs object created new :- " + cd);
        companyDao.storeCompanyDocs(cd);
        LOG.info("CompanyDocs object stored :- " + cd.getDocsid());     
      } 
    

    新代码

    if(cd == null)
      {
        cd = new CompanyDocs();//no-arg constructor
        c.setCompanyDocs(cd);
        cd.setCompany(c);
        LOG.info("CompanyDocs object created new :- " + cd);
        companyDao.storeCompanyDocs(cd);
        LOG.info("CompanyDocs object stored :- " + cd.getDocsid());     
      } 
    

    干得漂亮!!

    我发现此链接对解决此问题很有用

    Hibernate StaleStateException

    【讨论】:

      【解决方案2】:

      @PrimaryKeyJoinColumn 只能用在可选的一侧,所以去掉Company 上注解的@PrimaryKeyJoinColumn

      此外,@OneToOnemappedBy 属性应该引用标记@OneToOne 的实体的属性名称。所以应该是:

      @OneToOne(fetch = FetchType.LAZY, mappedBy = "company")
      

      【讨论】:

      • 抱歉,问题中有错字,mappedby 属性是公司。我也确实删除了公司方面的@PrimaryKeyJoinColumn,但这没关系。不过我终于解决了这个问题,请看下面我的回答。
      • 是这个记录的地方:“@PrimaryKeyJoinColumn 应该只用于可选的一面”。我对 OneToOne 映射也有一些问题
      • 嗨,克里斯,这只是我自己的推论和观察。顾名思义,@PrimaryKeyJoinColumn 表示当前表的 PK 将对另一个表有 FK 约束。因此,如果另一个表中不存在记录,我们无法在当前表中创建记录,因为当前表中的记录的 PK 没有可用值。因此,当前表是可选的一面
      猜你喜欢
      • 2011-06-12
      • 2011-02-16
      • 2021-05-26
      • 2020-03-09
      • 2011-05-08
      • 1970-01-01
      • 1970-01-01
      • 2014-08-14
      • 2014-11-22
      相关资源
      最近更新 更多