Reputation: 665
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
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