dounyy
dounyy

Reputation: 665

TransientObjectException when loading an object from database

I have an error that makes me go nuts. When I try to create a new object, Hibernate returns an error :

object references an unsaved transient instance - save the transient instance before flushing: EquipmentType; nested exception is org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before

I have the following Spring services :

CommandService

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;     
    }
}

EquipmentService

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);
    }
}

Note that all the methods of IEquipmentService and ICommandService are configured as transactional. My object Equipment is composed of a Model and an EquipmentType. Those objects refer already created objects in database, that is why I don't want to save them again.

Here is the Hibernate configuration :

<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 : As requested, here are the methods equals(..) and hashcode(..)

For Equipment

@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;
}

For Model and EquipmentType, those methods are not explicitly defined.

Can you help me to figure out why this error is returned when I call createCommand(..) ?

Upvotes: 0

Views: 223

Answers (1)

Mohit
Mohit

Reputation: 1755

I think the problem is that you set new EquipmentType and Model in your getEquipment method. As you haven't defined equals() and hashcode method in these 2 classes, hibernate is unable to compare these with already persisted objects. It sees them as detached (because equals() and hashcode() methods are indicating new objects instead of existing objects). That's why it ask you to save equipment type and model objects first.

I think you should override equals() & hashcode() properly in EquipmentType & Model.


EDIT

I think you shouldn't add new EquipmentType as you override it again in completeType() method. Why don't you change your API to

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

If you can't change APIs, you will have to persist new type and model first before hibernate flush is triggered.

Upvotes: 1

Related Questions