This isn't really a bug, but perhaps Hibernate should recover more gracefully. Here's how to reproduce it: Write some code that (1) saves an object, (2) sleeps for 30 seconds. Sneak behind hibernate's back and delete the object from the database. (3) call saveOrUpdateCopy on a copy of the object. Hibernate throws a NullPointerException.
The problem occurs in the doCopy code in SessionImpl:
At line 3807, persister.isUnsaved returns false because the object already has an identifier value...
Code:
if ( id==null && persister.isUnsaved(object) ) {
saveWithGeneratedIdentifier(object, Cascades.ACTION_COPY, copiedAlready);
return object; //TODO: handle its proxy (reassociate it, I suppose)
}
else {
... Then at line 3814, the code tries to get the object, gets back nothing. Seeng that 'result' is null, the code realizes the session was lying about the object being unsaved, and saves a new instance. 'result' is still null ...
Code:
Object result = get(clazz, id);
if (result==null) {
saveWithGeneratedIdentifier(object, Cascades.ACTION_COPY, copiedAlready);
}
else if ( Hibernate.getClass(result)!=clazz ) {
throw new WrongClassException("class of the given object did not match class of persistent copy", id, clazz);
}
//cascade first, so that all unsaved objects get saved before we actually copy
Cascades.cascade(this, persister, object, Cascades.ACTION_COPY, Cascades.CASCADE_ON_COPY, copiedAlready);
... Here's the problem. 'result' is still null when the code tries to unproxy it and get its property values. Things fall apart with a NullPointerException. Is the call to TypeFactory.copy still necessary after the previous call to saveWithGeneratedIdentifier?
Code:
Object target = unproxy(result);
//no need to handle the version differently
Object[] copiedValues = TypeFactory.copy(
persister.getPropertyValues(object),
persister.getPropertyValues(target),
persister.getPropertyTypes(),
this,
target
);
While I'm sifting through this code, I have another question. Shouldn't 'saveOrUpdateCopy' call interceptor.isUnsaved instead of persister.isUnsaved the same way that 'save' and 'update' do?
It's not hard to imagine a real (albeit stupid or implausible) situation where something like this behind-the-back delete would occur. I discovered this exception when I was doing some testing of another component.
Hibernate Version: 2.1.1
Name and version of the database you are using: Microsoft SQL Server 2000
Full stack trace of any exception that occurs:Code:
Thread [ExecuteThread: '14' for queue: 'weblogic.kernel.Default'] (Suspended (exception NullPointerException))
GeneratedMethodAccessor110.invoke(Object, Object[]) line: not available
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25
Method.invoke(Object, Object[]) line: 324
BasicPropertyAccessor$BasicGetter.get(Object) line: 96
ConstrainedEntityPersister(AbstractEntityPersister).getPropertyValues(Object) line: 249
SessionImpl.doCopy(Object, Serializable, Map) line: 3826
SessionImpl.saveOrUpdateCopy(Object) line: 3780
HibernatePersistenceManagerSLBean_2p157t_Impl(HibernatePersistenceManager).doSave(Persistable, boolean, boolean) line: 566
HibernatePersistenceManagerSLBean_2p157t_Impl(AbstractPersistenceManager).save(Persistable, String, boolean, boolean) line: 547
HibernatePersistenceManagerSLBean_2p157t_Impl(AbstractPersistenceManager).save(Persistable) line: 258
HibernatePersistenceManagerSLBean_2p157t_ELOImpl.save(Persistable) line: 349
DataObjectService_jfvdg7_Impl(DataObjectServiceSLBean).save(IDataObject) line: 152
DataObjectService_jfvdg7_EOImpl.save(IDataObject) line: 388
DataObjectServiceDelegate.save(IDataObject) line: 139
RepositoryDataService_3wn7w7_Impl(RepositoryDataServiceImpl).saveMetadataStorage(IMetadataStorage) line: 88
RepositoryDataService_3wn7w7_EOImpl.saveMetadataStorage(IMetadataStorage) line: 496
RepositoryDataServiceDelegate.saveMetadataStorage(IMetadataStorage) line: 96
RepositoryManager_vlvjzr_Impl(RepositoryManagerSLBean).addOrReplaceMetadata(Metadata) line: 390
RepositoryManager_vlvjzr_EOImpl.addOrReplaceMetadata(Metadata) line: 376
RepositoryManager_vlvjzr_EOImpl_WLSkel.invoke(int, InboundRequest, OutboundResponse, Object) line: not available
ClusterableServerRef(BasicServerRef).invoke(RuntimeMethodDescriptor, InboundRequest, OutboundResponse) line: 466
ClusterableServerRef(ReplicaAwareServerRef).invoke(RuntimeMethodDescriptor, InboundRequest, OutboundResponse) line: 108
BasicServerRef$1.run() line: 409
AuthenticatedSubject.doAs(AbstractSubject, PrivilegedExceptionAction) line: 353
SecurityManager.runAs(AuthenticatedSubject, AuthenticatedSubject, PrivilegedExceptionAction) line: 144
ClusterableServerRef(BasicServerRef).handleRequest(InboundRequest) line: 404
BasicExecuteRequest.execute(ExecuteThread) line: 30
ExecuteThread.execute(ExecuteRequest) line: 197
ExecuteThread.run() line: 170