【问题标题】:TransientObjectException when loading an object from database从数据库加载对象时出现 TransientObjectException
【发布时间】:2015-11-27 13:27:57
【问题描述】:

我有一个让我发疯的错误。当我尝试创建一个新对象时,Hibernate 返回一个错误:

object 引用了一个未保存的瞬态实例 - 在刷新之前保存瞬态实例: EquipmentType;嵌套异常是 org.hibernate.TransientObjectException: 对象引用了一个未保存的瞬态实例 - 在此之前保存瞬态实例

我有以下 Spring 服务:

命令服务

public class CommandService implements ICommandService {
    @Override
    public Command createCommand(String name, String eqpName) {
        Command cmd = new Command();        
        Equipment eqp = getEquipment(eqpName);

        cmd.setEquipment(eqp);      
        cmd.setName(name);

        return cmd;
    }

    @Override
    public Equipment getEquipment(String eqpName) {
        IEquipmentService eqpService = (IEquipmentService) SPRING_CONTEXT.getBean("EquipmentService");

        Equipment eqp = eqpService.findEquipmentByName(eqpName);

        EquipmentType eqpType = new EquipmentType();
        eqpType.setName("MyType");
        eqp.setType(eqpType);

        Model model = new Model();
        model.setName("MyModel");
        eqp.setModel(model);

        eqpService.saveEquipment(eqp);

        return eqp;     
    }
}

设备服务

public class EquipmentService implements IEquipmentService {

    private DAO dao; // This DAO is autowired

    @Override
    public void saveEquipment(Equipment eqp) {
        completeModel(eqp);
        completeEqpType(eqp);

        ((EquipmentDAO) dao).merge(eqp);
    }

    private void completeModel(Equipment eqp) {
        IModelService modelService = (IModelService) SPRING_CONTEXT.getBean("ModelService");
        Model result = modelService.findModelByName(eqp.getModel().getName());  // Fails here

        eqp.setModel(result);
    }

    private void completeEqpType(Equipment eqp) {
        IEquipmentTypeService eqpTypeService = (IEquipmentTypeService) SPRING_CONTEXT.getBean("EquipmentTypeService");
        EquipmentType result = eqpTypeService.findEquipmentTypeByName(eqp.getType().getName());

        eqp.setType(result);
    }
}

请注意,IEquipmentServiceICommandService 的所有方法都配置为事务性的。 我的对象EquipmentModelEquipmentType 组成。 这些对象引用了数据库中已经创建的对象,这就是我不想再次保存它们的原因。

这里是休眠配置:

<class name="Equipment" table="equipment" lazy="false">
        <id name="equipmentId" type="java.lang.Integer">
            <column name="EQUIPMENT_ID" />
            <generator class="native" />
        </id>
        <many-to-one name="type" class="EquipmentType" fetch="join" not-null="true">
            <column name="TYPE_ID" not-null="true" />
        </many-to-one>
        <many-to-one name="model" class="Model" fetch="join" not-null="true">
            <column name="MODEL_ID" not-null="true" />
        </many-to-one>
</class>

EDIT:根据要求,这里是equals(..)hashcode(..)的方法

对于设备

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((equipmentId == null) ? 0 : equipmentId.hashCode());
    result = prime * result + ((type == null) ? 0 : type.hashCode());
    result = prime * result + ((model == null) ? 0 : model.hashCode());
    return result;
}

@Override
public boolean equals(Object obj) {
    if (obj == this) {
        return true;
    }

    if (obj instanceof Equipment) {
        Equipment eqp = (Equipment) obj;
        return new EqualsBuilder().append(this.equipmentId, eqp.getEquipmentId()).isEquals();
    }

    return false;
}

对于 Model 和 EquipmentType,这些方法没有明确定义。

您能帮我弄清楚为什么当我调用createCommand(..) 时会返回此错误吗?

【问题讨论】:

  • 你是如何为你的模型类定义 equals() 和 hashcode() 方法的?你能添加那些有问题的细节吗?
  • @mohit 我编辑了我的原始帖子。请注意,EqualsBuilder 由 Apache Commons 提供。

标签: java spring hibernate


【解决方案1】:

我认为问题在于您在getEquipment 方法中设置了新的EquipmentTypeModel。由于您没有在这 2 个类中定义 equals() 和 hashcode 方法,hibernate 无法将它们与已经持久化的对象进行比较。它认为它们是分离的(因为 equals() 和 hashcode() 方法指示新对象而不是现有对象)。这就是为什么它要求您先保存设备类型和模型对象。

我认为你应该在 EquipmentType & Model 中正确地覆盖 equals() & hashcode()。


编辑

我认为您不应该添加新的 EquipmentType,因为您在 completeType() 方法中再次覆盖它。你为什么不把你的 API 改成

EquipmentService#saveEquipment(Equipment e, Sting type, String modelName){
    EquipmentType t = findType();
    Model m = findModel();
    e.setType(t);
    e.setModel(m);
}

如果您无法更改 API,则必须在触发休眠刷新之前先持久化新类型和模型。

【讨论】:

  • 但是我的 equals() 方法会比较这些对象的 ID。在getEquipment() 中,我的对象没有任何 ID,所以它们仍然会被认为是分离的,不是吗?
  • 新对象保持分离状态,因为在触发刷新之前您没有保存它们。每当执行选择查询时,都会自动触发刷新。我已经编辑了答案以提供解决方法。
  • 我不明白的是,如果事务还没有结束,为什么hibernate必须刷新?
  • Hibernate 并不总是在事务提交时刷新。如果 hibernate 必须运行一个 select,它必须首先将内存中的所有更改与数据库同步,以便内存中的对象也可以出现在搜索结果中。
  • 所以你告诉我没有办法创建一个 Java 对象,然后在不持久化该对象的情况下对数据库执行选择?是不是很奇怪?
猜你喜欢
  • 2020-08-06
  • 1970-01-01
  • 2011-04-14
  • 1970-01-01
  • 1970-01-01
  • 2019-03-13
  • 2011-05-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多