Hibernate可选一对一关联问题:增删改查异常解决求助
问题:Hibernate可选一对一映射无法同时支持增删改查
场景很简单:Company表中的公司可以关联一份PIA表中的PIA协议,但并非必须。我花了一整天在Stack Overflow和谷歌上搜相关内容,愣是没找到能同时支持SELECT、INSERT和UPDATE的解决方案。找到了Hibernate一对一和一对多映射的文档,但示例太模糊了。
表结构
create_company.sql
CREATE TABLE company ( id SMALLINT AUTO_INCREMENT, name varchar(60) default NULL, PRIMARY KEY (id) );
create_pia.sql
CREATE TABLE pia ( company_id smallint, agreement_number varchar(50), PRIMARY KEY (company_id), CONSTRAINT pia_ibfk_1 FOREIGN KEY (company_id) REFERENCES company (id) );
注意:看起来得加cascade="all"才能让UPDATE生效。
尝试过的配置及错误
配置1:one-to-one映射
<one-to-one name="pia" class="something.something.Pia" cascade="all"/>
- 结果:SELECT和UPDATE正常,但INSERT失败,报错:
java.sql.SQLException: Cannot add or update a child row: a foreign key constraint fails (`iti101088_15112`.`pia`, CONSTRAINT `pia_ibfk_1` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`))
配置2:many-to-one映射
<many-to-one name="pia" class="something.something.Pia" cascade="all"/>
- 结果:查询公司时出错:
Caused by: java.sql.SQLException: Unknown column 'company0_.pia' in 'field list'
配置3:带property-ref的many-to-one
<many-to-one name="pia" class="something.something.Pia" cascade="all" property-ref="companyId"/>
- 结果:同样报错找不到
company0_.pia列
配置4:指定column的many-to-one
<many-to-one name="pia" class="something.something.Pia" cascade="all" property-ref="companyId" column="id"/>
- 结果:查询时出现
NullPointerException
配置5:参考建议用伪many-to-one
看到有建议说“Hibernate不支持可选一对一(参考HHH-2007),得用not-null="false"的伪many-to-one代替”,用了如下配置:
<many-to-one name="pia" class="something.something.Pia" cascade="all" not-null="false"/>
- 结果:还是报找不到
company0_.pia列
配置6:嵌套column的many-to-one
<many-to-one name="pia" class="something.something.Pia" cascade="all"> <column name="id" not-null="false"/> </many-to-one>
- 结果:出现序列化错误:
Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: could not initialize proxy [something.something.Pia#1] - no Session; nested exception is com.fasterxml.jackson.databind.JsonMappingException: could not initialize proxy [something.something.Pia#1] - no Session (through reference chain: something.something.Company["pia"]->something.something.Pia$HibernateProxy$54WqyDsD["agreementNumber"])
相关代码
Company.hbm.xml
<class name="something.something.Company" table="COMPANY"> <id name="id" column="ID"> <generator class="increment"/> </id> <property name="name" column="NAME"/> <many-to-one name="pia" class="something.something.Pia" cascade="all"/> </class>
Pia.hbm.xml
<class class="something.something.Pia" table="PIA"> <id name="companyId" column="COMPANY_ID"/> <property name="agreementNumber" column="AGREEMENT_NUMBER"/> </class>
Company.java
public class Company implements Serializable { private static final long serialVersionUID = 1L; private int id; private String name; private Pia pia; // 原代码漏定义该属性 public Company() { } public Company(int id, String name, Pia pia) { super(); this.id = id; this.name = name; this.pia = pia; // 原代码赋值错误,已修正 } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Pia getPia() { return pia; } public void setPia(Pia pia) { this.pia = pia; } }
Pia.java
public class Pia implements Serializable { private int companyId; private String agreementNumber; public Pia() { } public Pia(int companyId, String agreementNumber) { this.companyId = companyId; this.agreementNumber = agreementNumber; } public int getCompanyId() { return companyId; } public void setCompanyId(int companyId) { this.companyId = companyId; } public String getAgreementNumber() { return agreementNumber; } public void setAgreementNumber(String agreementNumber) { this.agreementNumber = agreementNumber; } }
DAO代码
查询
@Override public Company findCompanyByCompanyId(int companyId) { return (Company) DataAccessUtils.singleResult(template.find("from Company where id=?0", companyId)); }
新增
@Override @Transactional public int insertCompany(Company company) { int result = -1; try { template.save(company); result = company.getId(); } catch (Exception e) { logger.error(e.getMessage(), e); } return result; }
更新
@Override @Transactional public int updateCompany(Company myCompany) { int result = 0; try { template.update(myCompany); result = 1; } catch (Exception e) { logger.error(e.getMessage(), e); result = -1; } return result; }
解决方案
核心问题是未正确配置可选一对一的关联逻辑,结合你的表结构(PIA以company_id为主键关联Company的id),正确配置如下:
1. 修正映射文件
Company.hbm.xml
将many-to-one配置为带唯一约束的关联,模拟一对一关系,同时解决懒加载序列化问题:
<class name="something.something.Company" table="COMPANY"> <id name="id" column="ID"> <generator class="increment"/> </id> <property name="name" column="NAME"/> <!-- 可选一对一配置:唯一约束保证一对一,column对应PIA的company_id即当前实体id --> <many-to-one name="pia" class="something.something.Pia" cascade="all" column="id" unique="true" not-null="false" fetch="join"/> </class>
Pia.hbm.xml
配置主键生成策略为foreign(依赖Company的id),并添加反向关联:
<class name="something.something.Pia" table="PIA"> <id name="companyId" column="COMPANY_ID"> <generator class="foreign"> <param name="property">company</param> </generator> </id> <property name="agreementNumber" column="AGREEMENT_NUMBER"/> <!-- 反向关联Company --> <one-to-one name="company" class="something.something.Company" constrained="true"/> </class>
2. 修正实体类
给Pia类添加Company关联属性,实现双向绑定:
public class Pia implements Serializable { private int companyId; private String agreementNumber; private Company company; // 新增反向关联 public Pia() { } public Pia(int companyId, String agreementNumber) { this.companyId = companyId; this.agreementNumber = agreementNumber; } // 新增getter/setter public Company getCompany() { return company; } public void setCompany(Company company) { this.company = company; if(company != null) { this.companyId = company.getId(); // 同步主键 } } // 原有getter/setter不变 public int getCompanyId() { return companyId; } public void setCompanyId(int companyId) { this.companyId = companyId; } public String getAgreementNumber() { return agreementNumber; } public void setAgreementNumber(String agreementNumber) { this.agreementNumber = agreementNumber; } }
3. 调整新增逻辑
插入带PIA的Company时,必须双向绑定关联关系:
// 示例代码 Company company = new Company(); company.setName("Test Corp"); Pia pia = new Pia(); pia.setAgreementNumber("ABC123"); pia.setCompany(company); // 绑定反向关联 company.setPia(pia); companyDao.insertCompany(company);
完成以上配置后,SELECT、INSERT、UPDATE操作均可正常执行,同时支持可选一对一的关联需求。
内容的提问来源于stack exchange,提问作者the_overflowing_stack
相关产品推荐
相关产品推荐

